- Home /
How to send POST using WWWForm?
Hello, this is my first time dealing with POST and WWForm. Im using 2019.3.0f3 on Windows 10. I want to send an image and a bunch of other variables that are needed by an API but the responseCode gives back 404 . Which means that the url/server cant be found.
My code:
byte[] imagebytes = File.ReadAllBytes(imagepath);
/// Create form data including adding binary for photo.
WWWForm form = new WWWForm();
//component_type is a string array : string[] component_type = new string[] {"eyes"}
form.AddField("components", component_type[0]);
form.AddBinaryData("image", imagebytes, imagepath, "image/jpg");
form.AddField("partner_id", Partner_ID);
form.AddField("skintone", skin);
form.AddField("gender", gender);
// Upload the photo.
UnityWebRequest uploadPhoto = UnityWebRequest.Post(BaseUrl + ActionUrl + "?component=" + component_type[0], form);
uploadPhoto.SetRequestHeader("accept", "application/json");
uploadPhoto.SetRequestHeader("Content-Type", "multipart/form-data;");
yield return new WaitForSecondsRealtime(0.5f);
yield return uploadPhoto.SendWebRequest();
string url = uploadPhoto.url;
Debug.Log(uploadPhoto.method);
Debug.Log("post url is " + url);
Debug.Log(uploadPhoto.responseCode);
Debug.Log(uploadPhoto.error);
BaseUrl, ActionUrl and Partner_ID are set as public string variables. BaseUrl and ActionUrl is needed so the script knows where to send it to. Partner_ID is a sort of authorization key for testing purposes in the API that Im trying to connect.
The Debug.Log sends (in order) : POST, post url is BaseUrl/ActionUrl/?component=eyes, 404, HTTP/1.1 404 Not Found
So does this mean the base url and action url are wrong? like there's something wrong with it or is it my form?
In the API, when we try it out manually the post is like the image attached. blue is BaseUrl, red is ActionUrl, and yellow is Partner_ID
Answer by Bunny83 · Feb 07, 2020 at 09:10 AM
First of all, remove this line:
uploadPhoto.SetRequestHeader("Content-Type", "multipart/form-data;");
You destroy your request this way. The "Content-Type" header of a multipart request requires a boundary which the WWWForm correctly introduces. You overwrite it. It could be the reason why the server returns a 404, though it's a strange response if that's the reason. It should answer with a "400 Bad Request". However different servers can have different ways to handle errors. Maybe for security reasons. Maybe just a lazy programmer.
Hi, okay i commented that line. And then i changed the code a smidge like so :
byte[] imagebytes = File.ReadAllBytes(imagepath);
/// Create form data including adding binary for photo.
WWWForm form = new WWWForm();
form.AddBinaryData("image", imagebytes, imagepath, "image/jpg");
form.AddField("partner_id", Partner_ID);
byte[] formbytes = form.data;
UnityWebRequest uwr = UnityWebRequest.Put(BaseUrl + ActionUrl + "/?component=" + component_type[0], formbytes);
uwr.method = "POST";
uwr.url = BaseUrl + ActionUrl + "/?component=" + component_type[0];
uwr.downloadHandler = new DownloadHandlerBuffer();
uwr.SetRequestHeader("accept", "application/json");
//uwr.SetRequestHeader("Content-Type", "multipart/form-data;");
yield return uwr.SendWebRequest();
if (uwr.isDone)
{
if (uwr.isNetworkError)
{
Debug.Log("Error While Sending: " + uwr.error);
}
else
{
Debug.Log("Received: " + uwr.downloadHandler.text + " error code is " + uwr.responseCode);
string url = uwr.url;
Debug.Log(uwr.method);
Debug.Log("post url is " + url);
Debug.Log(uwr.error);
}
}
in console i got : Received: {"message":"Partner ID required"} error code is 404
So i am missing something. but i've already added in the form. "image" and "partner_id" is the field name in the API. and as i mentioned, ive set the partner_id as a public string.
This makes no sense either. When you use a WWW form you have to either use the form in the request or use it's data and headers. Again a multipart request is splitted into sections which are splitted by a random boundary string. That boundary has to be mentioned in the "Content-Type" header. The headers are completely seperated from the message body (aka form.data). Currently you just grab the message body and post it as a single chunk of data without providing any of the headers that the WWWForm class has setup.
Also why do you use "UnityWebRequest.Put" when you then change the request method manually to POST?
Also why do you use "UnityWebRequest.Put" when you then change the request method manually to POST?
Was just trying things out. Because while searching i found a Q&A, and someone said that POST messes up something(?) and to use PUT then manually change it to POST. So was seeing if that helps.
But you're reply to remove the content type header is correct. i commented that out and i used form ins$$anonymous$$d of formbytes in the POST. thank you.
Answer by ndmrzk · Feb 07, 2020 at 09:28 AM
I found this thread and tried to implement it into my code. Instead of using WWWForm method they used List Now it looks like this (thanks to @austingraham @michaelneil for sharing)
byte[] imagebytes = File.ReadAllBytes(imagepath);
List<IMultipartFormSection> requestData = new List<IMultipartFormSection>();
requestData.Add(new MultipartFormFileSection("image", imagebytes, imagepath, "image/jpg"));
requestData.Add(new MultipartFormFileSection("partner_id", Partner_ID));
//generate a unique boundary
byte[] boundary = UnityWebRequest.GenerateBoundary();
//serialize form fields into byte[] => requires a bounday to put in between fields
byte[] formSections = UnityWebRequest.SerializeFormSections(requestData, boundary);
// my termination string consisting of CRLF--{boundary}--
byte[] terminate = Encoding.UTF8.GetBytes(String.Concat("\r\n--",Encoding.UTF8.GetString(boundary), "--"));
// Make my complete body from the two byte arrays
byte[] body = new byte[formSections.Length + terminate.Length];
Buffer.BlockCopy(formSections, 0, body, 0, formSections.Length);
Buffer.BlockCopy(terminate, 0, body, formSections.Length, terminate.Length);
// Set the content type - NO QUOTES around the boundary
string contentType = String.Concat("multipart/form-data; boundary=", Encoding.UTF8.GetString(boundary));
UnityWebRequest request = new UnityWebRequest(BaseUrl + ActionUrl + "/?component=" + component_type[0])
{
uploadHandler = new UploadHandlerRaw(body)
{
contentType = "multipart/form-data; boundary=\"" + System.Text.Encoding.UTF8.GetString(boundary) + "\""
},
downloadHandler = new DownloadHandlerBuffer(),
method = "POST"
};
//request.SetRequestHeader("Content-Type", "multipart/form-data");
yield return request.SendWebRequest();
if (request.isDone)
{
if (request.isNetworkError)
{
Debug.Log("Error While Sending: " + request.error);
}
else
{
Debug.Log("Received: " + request.downloadHandler.text + " error code is " + request.responseCode);
string url = request.url;
Debug.Log(request.method);
Debug.Log("post url is " + url);
Debug.Log(request.error);
}
}
For the first debug in the else (Received....) i get a really weird html, which mostly looks like word vomit to me (if someone can explain whats happening that would be great):
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Error</title>
</head>
<body>
<pre>MulterError: Unexpected field<br> at wrappedFileFilter (/home/ubuntu/dev/revieve-server/node_modules/multer/index.js:40:19)<br> at Busboy.<anonymous> (/home/ubuntu/dev/revieve-server/node_modules/multer/lib/make-middleware.js:114:7)<br> at Busboy.emit (events.js:182:13)<br> at Busboy.EventEmitter.emit (domain.js:442:20)<br> at Busboy.emit (/home/ubuntu/dev/revieve-server/node_modules/busboy/lib/main.js:38:33)<br> at PartStream.<anonymous> (/home/ubuntu/dev/revieve-server/node_modules/busboy/lib/types/multipart.js:213:13)<br> at PartStream.emit (events.js:182:13)<br> at PartStream.EventEmitter.emit (domain.js:442:20)<br> at HeaderParser.<anonymous> (/home/ubuntu/dev/revieve-server/node_modules/dicer/lib/Dicer.js:51:16)<br> at HeaderParser.emit (events.js:182:13)</pre>
</body>
</html>
and, i assume because of the weird html thing, now i get error 500 which is internal server error.
First of all do not post questions in answers. An answer should answer the question that was asked at the top. If you want to add details to your question, edit your question. However your question should be asked detailed and specific in the first place. Please do not constantly editing and changing the question. That makes it extremely difficult to actually answer the question.
Why would you manually build a HTTP request yourself? That's exactly what the WWWForm class does for you. There are countless things you can do wrong this way. Specifically you have quoted your boundary string in the contentType of your uploadHandler. There shouldn't be any quotes.
Apart from that the result you get is just your nodejs server that is choking on what you send him. It throws a javascript exception which is just returned to you as an HT$$anonymous$$L page. Since the error happens in the "HeaderParser" it's most likely your quoted boundary string.
Sorry but this question turns into a try-and-error witch hunt. UnityAnswers is not a forum to discuss issues. You should ask a clear and detailed question and you (hopefully) get clear and detailed answers. For testing your endpoints I highly recommend to use HTTP and wireshark to see what's going on. If you can't use HTTP and have to use HTTPS it gets a lot more difficult to debug any potential issues. However there are test servers like this one which you can use in exchange of your actual server. The point of those is that they will return your whole request data so you can see what and how that arrived on the server.