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

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.

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("

–",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
at wrappedFileFilter (/home/ubuntu/dev/revieve-server/node_modules/multer/index.js:40:19)
at Busboy.<anonymous> (/home/ubuntu/dev/revieve-server/node_modules/multer/lib/make-middleware.js:114:7)
at Busboy.emit (events.js:182:13)
at Busboy.EventEmitter.emit (domain.js:442:20)
at Busboy.emit (/home/ubuntu/dev/revieve-server/node_modules/busboy/lib/main.js:38:33)
at PartStream.<anonymous> (/home/ubuntu/dev/revieve-server/node_modules/busboy/lib/types/multipart.js:213:13)
at PartStream.emit (events.js:182:13)
at PartStream.EventEmitter.emit (domain.js:442:20)
at HeaderParser.<anonymous> (/home/ubuntu/dev/revieve-server/node_modules/dicer/lib/Dicer.js:51:16)
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.

Hi, found this thread and it’s helped to get me on the right track, but I’ve found myself stuck hitting a 415 error (unsupported media type) trying to upload to Confluence’s REST API specifically. I did deviate from the above code a bit (UnityWebRequest.Post() would not accept a byte input, and if I didn’t include the “Content-Type” header I got a 401 (access) error so I kept it. Also, mind the pseudocode where I substituted <> for my actual token.

If I’m understanding this, a 415 error is telling me the media type isn’t recognizable. What might be be doing wrong here?

Thanks so much!

 IEnumerator PostImageToURL(string imageName, string url) {
            WWWForm form = new WWWForm();
            form.AddBinaryData("file", File.ReadAllBytes(imageName), imageName, "image/png");

            if (File.Exists(testImage)) {
                UnityEngine.Debug.Log("UWR>>> Uploading file " + imageName);
            } else {
                UnityEngine.Debug.LogError("UWR>>> Could not find " + imageName);
            }

            //byte[] formbytes = form.data;

            UnityWebRequest uwr = UnityWebRequest.Post(url, form);
            uwr.method = "POST";
            uwr.url = url;
            // for basic auth with confluence token, generate a base64 identifier with: echo -n <<EMAIL>>:<<TOKEN_ID>> | base64
            uwr.SetRequestHeader("Authorization", "Basic <<base64 identifier>>");
            uwr.SetRequestHeader("Content-Type", "application/json");
            uwr.SetRequestHeader("Accept", "application/json");
            uwr.downloadHandler = new DownloadHandlerBuffer();
            yield return uwr.SendWebRequest();
            
            if (uwr.isDone) {
                if (uwr.isNetworkError) {
                    UnityEngine.Debug.Log("UWR>>> Error While Sending: " + uwr.error);
                } else {
                    UnityEngine.Debug.Log("UWR>>> Received: " + uwr.downloadHandler.text + " error code is " + uwr.responseCode);
                    string uwrURL = uwr.url;
                    UnityEngine.Debug.Log(uwr.method);
                    UnityEngine.Debug.Log("UWR>>> post url is " + uwrURL);
                    UnityEngine.Debug.Log(uwr.error);

                    uwr.Dispose();
                }
            }
        }