Search Unity

POSTing raw json into UnityWebRequest

Discussion in 'Web' started by cogentEd, Apr 15, 2016.

  1. cogentEd

    cogentEd

    Joined:
    Aug 3, 2015
    Posts:
    6
    I'm having trouble getting anything other than a 400 status from my webserver when using UnityWebRequest and json.

    Here's what I'm doing:
    Code (CSharp):
    1. //Example string param= "{"username":"aaa","app_key":"webGLTesting","password":"aaa"}";
    2.  
    3. UnityWebRequest CreateUnityWebRequest(string url, string param) {
    4.             UnityWebRequest requestU= new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
    5.             byte[] bytes= GetBytes(param);
    6.             UploadHandlerRaw uH= new UploadHandlerRaw(bytes);
    7.             uH.contentType= "application/json";
    8.             requestU.uploadHandler= uH;
    9.             CastleDownloadHandler dH= new CastleDownloadHandler();
    10.             requestU.downloadHandler= dH; //need a download handler so that I can read response data
    11.             return requestU;
    12. }
    13. class CastleDownloadHandler: DownloadHandlerScript {
    14.            public delegate void Finished();
    15.            public event Finished onFinished;
    16.  
    17.            protected override void CompleteContent ()
    18.            {
    19.                       UnityEngine.Debug.Log("CompleteContent()");
    20.                       base.CompleteContent ();
    21.                       if (onFinished!= null) {
    22.                                  onFinished();
    23.                       }
    24.            }
    25. }
    26. protected static byte[] GetBytes(string str){
    27.            byte[] bytes = Encoding.UTF8.GetBytes(str);
    28.            return bytes;
    29. }
    I can successfully hit my server, but it returns Status 400. I tried the same thing with the WWW class and it worked! The below is passed the exact same input string param as above.
    Code (CSharp):
    1. UnityEngine.WWW CreateUnityWebRequestV6(string url, string param) {
    2.             Dictionary<string, string> headers = new Dictionary<string, string>();
    3.             headers.Add("Content-Type", "application/json");
    4.             byte[] body = Encoding.UTF8.GetBytes(param);
    5.             UnityEngine.WWW www = new UnityEngine.WWW(url, body, headers);
    6.             return www;
    7. }
    Also putting the json values in myself through form data also works with UnityWebRequest.
    Code (CSharp):
    1. UnityWebRequest CreateUnityWebRequestV3(string url, string param) {
    2.             UnityEngine.WWWForm form = new UnityEngine.WWWForm();
    3.             form.AddField("username", "aaa");
    4.             form.AddField("password", "aaa");
    5.             form.AddField("app_key", "webGLTesting");
    6.             UnityWebRequest requestU= UnityWebRequest.Post(url, form);
    7.             requestU.SetRequestHeader("Accept", "application/json");
    8.             requestU.uploadHandler.contentType= "application/json";
    9.             CastleDownloadHandler dH= new CastleDownloadHandler();
    10.             requestU.downloadHandler= dH; //need a download handler so that I can read response data
    11.             return requestU;
    12. }
    I already have a working system using a preformated json string using JsonUtility and .Net. I would like to avoid rewriting everything and use form data. I am also assuming the WWW class is on the way out and want to use the newer UnityWebRequest.

    What am I doing wrong with UnityWebRequest and passing my byte[] into UploadHandlerRaw?
     
  2. cogentEd

    cogentEd

    Joined:
    Aug 3, 2015
    Posts:
    6
    It's been about a month with this issue. I was simply using WWW, but with CORS WWW won't supply a status. I'm now back to looking at using UnityWebRequest.

    Anyone have ideas on why the byte[] won't work with UploadHandlerRaw?
     
    dtnojiji likes this.
  3. cogentEd

    cogentEd

    Joined:
    Aug 3, 2015
    Posts:
    6
    Progress! I no longer get 400 errors. The issue was that the http request's content-type wasn't set.

    You'll notice in my original code below that I set the UploadHandlerRaw's content-type directly (line 5)...
    Code (CSharp):
    1. UnityWebRequest CreateUnityWebRequest(string url, string param) {
    2.             UnityWebRequest requestU= new UnityWebRequest(url, UnityWebRequest.kHttpVerbPOST);
    3.             byte[] bytes= GetBytes(param);
    4.             UploadHandlerRaw uH= new UploadHandlerRaw(bytes);
    5.             uH.contentType= "application/json"; //this is ignored?
    6.             requestU.uploadHandler= uH;
    7.             CastleDownloadHandler dH= new CastleDownloadHandler();
    8.             requestU.downloadHandler= dH;
    9.             return requestU;
    10. }
    I thought my problem was setting the contentType too early. So I moved it after setting UnityWebRequest.upload handler, like below. Sadly, content-type was still ignored.
    Code (CSharp):
    1. UnityWebRequest CreateUnityWebRequest(string url, string param) {
    2.             ...
    3.             UploadHandlerRaw uH= new UploadHandlerRaw(bytes);
    4.             requestU.uploadHandler= uH;
    5.             uH.contentType= "application/json"; //this is also ignored.
    6.             ...
    7. }
    Quite surprising considering Unity's documentation of UploadHandler.contentType:
    As you can see from my first code snippet, I never set a custom Content-Type header, so the uploadHandler.contentType should be honored.

    Anyways, setting request header manually DOES work.
    Code (CSharp):
    1. UnityWebRequest CreateUnityWebRequest(string url, string param) {
    2.             ...
    3.             UploadHandlerRaw uH= new UploadHandlerRaw(bytes);
    4.             requestU.uploadHandler= uH;
    5.              requestU.SetRequestHeader("Content-Type", "application/json");
    6.             ...
    7. }
    Hopefully this will help someone out there.
     
  4. Freaking-Pingo

    Freaking-Pingo

    Joined:
    Aug 1, 2012
    Posts:
    310
    Thank you. I am currently trying to set up a Unity project with Django, but I had trouble sending a PUT request, receiving a HTTP 415 response (Unsupported Media Type). I also tried to create a new UploadHandlerRaw and attempted to set the uploadHandler.contenttype variable manually, then adding the uploadhandler to the UnityWebRequest object, however this action seemed to do no effect:

    Code (CSharp):
    1.         UnityWebRequest webRequest = UnityWebRequest.Put(destination, formData);
    2.         UploadHandler customUploadHandler = new UploadHandlerRaw(System.Text.Encoding.UTF8.GetBytes(formData));
    3.         customUploadHandler.contentType = "application/json";
    4.         webRequest.uploadHandler = customUploadHandler;
    5.         SendRequest(webRequest);
    Setting the Content Type using the SetRequestHeader did the trick for me:

    Code (CSharp):
    1.         UnityWebRequest webRequest = UnityWebRequest.Put(destination, formData);
    2.         webRequest.SetRequestHeader("Content-Type", "application/json");
    3.         SendRequest(webRequest);
     
  5. janus007

    janus007

    Joined:
    Apr 12, 2016
    Posts:
    5
    UnityWebRequest is so tied to MonoBehavior and mainthread so it is impossible to create a thread and let that handle the download/ upload.
    I'm baffled that the Unity-crew continue their development without any consideration for the more seasoned programmers out there. Furthermore they know about Microsoft .NET Core and the upcoming transition to .NET 4.6 and still they manage to produce such utterly crap.

    UnityWebRequest == WWW with new namespace.
     
    Karsten and jvalencia like this.
  6. ttesla

    ttesla

    Joined:
    Feb 23, 2015
    Posts:
    16
    Thanks @Freaking-Pingo for "SetRequestHeader." It made my Put / Post worked. Here is my put method:

    Code (CSharp):
    1. IEnumerator Post()
    2.     {
    3.         Building b = new Building { Id="1212", Name="InsertCoin.info", X= 000.1f };
    4.         string postData = JsonConvert.SerializeObject(b);
    5.         byte[] bytes = GetBytes(postData);
    6.  
    7.         using (UnityWebRequest www = UnityWebRequest.Put("http://localhost:57459/api/building/?id=66", bytes))
    8.         {
    9.             www.SetRequestHeader("Content-Type", "application/json");
    10.  
    11.             yield return www.Send();
    12.  
    13.             if (www.isError)
    14.             {
    15.                 Debug.Log(www.error);
    16.             }
    17.             else
    18.             {
    19.                 Debug.Log(www.downloadHandler.text);
    20.             }
    21.         }
    22.     }
     
    Max_Aigner and rakkarage like this.
  7. dylan_shft

    dylan_shft

    Joined:
    Apr 24, 2017
    Posts:
    1
    I recently tried this with the recent 5.6.1 stable. I have a working solution for POST/PATCH/PUT. You just need to ensure you're server supports the "X-HTTP-Method-Override" header.

    Also UnityWebRequest doesn't support PATCH out of the box for a true REST interface, this example does.

    Code (CSharp):
    1.  
    2.            UnityWebRequest request;
    3.  
    4.            switch (method) {
    5.                 case "POST":
    6.                 case "PATCH":
    7.                 // Defaults are fine for PUT
    8.                 case "PUT":
    9.                     byte[] bytes = Encoding.UTF8.GetBytes(requestBodyJsonString);
    10.                     request = UnityWebRequest.Put(url, bytes);
    11.                     request.SetRequestHeader("X-HTTP-Method-Override", method);
    12.                     break;
    13.  
    14.                 case "GET":
    15.                     // Defaults are fine for GET
    16.                     request = UnityWebRequest.Get(url);
    17.                     break;
    18.  
    19.                 case "DELETE":
    20.                     // Defaults are fine for DELETE
    21.                     request = UnityWebRequest.Delete(url);
    22.                     break;
    23.  
    24.                 default:
    25.                     throw new Exception("Invalid HTTP Method");
    26.           }
    27.  
    28.           request.SetRequestHeader("accept", "application/json; charset=UTF-8");
    29.           request.SetRequestHeader("content-type", "application/json; charset=UTF-8");
    30.  
    Hope this helps people with the new upgrade to 5.6.1.
     
    Last edited: May 20, 2017
  8. YuKuk

    YuKuk

    Joined:
    Nov 9, 2015
    Posts:
    1
    I manage to send the POST request and the response from my server is OK (200).

    However I don't manage to get the JSON on the server side. I did a check to see if content-type is "application/json" and it turns out the request's content-type is not recognized as "application/json". This prevents me from getting the json that I send from Unity. The API is written in PHP.

    Is there something else that we could add in the request? Did anyone else encounter the same or similar problem?

    Thanks!

    [UPDATE]

    I managed to solve my issue. After having tried different solutions as proposed above by @cogentEd, @ttesla and @dylan_shft I managed to set the content-type to 'application/json' and have it recognized as such by PHP.

    I had to assign uploadHandler's content type which I then assigned to the request's uploadHandler as proposed by @Freaking-Pingo - thx!

    For some reason setting the content type directly to UnityWebRequest did not work for me.
     
    Last edited: Jun 8, 2017
  9. pretender

    pretender

    Joined:
    Mar 6, 2010
    Posts:
    865
    Hi guys! I am also try to upload file to server, but I don't know how to handle back end? Is there somewhere minimal example how to make it work?

    What I want to do is to upload json from unity and download from server (I want to be able to update data from unity and upload it to server and other clients to be able to download it from server)

    All I want is minimal example with one field in JSON and most simple back end part how it's done. There are a lot of sources on the web but complex for newbie :)

    Thanks!
     
    dan_ginovker likes this.
  10. evoros

    evoros

    Joined:
    Dec 2, 2015
    Posts:
    3
    This didn't work for me, I'm guessing my server doesn't support this override.

    However, I was able to get the above idea to work (hack the PUT into a POST) simply by doing this
    Code (CSharp):
    1. request.method = "POST";
    instead of
    Code (CSharp):
    1. request.SetRequestHeader("X-HTTP-Method-Override", method);
     
  11. enne30

    enne30

    Joined:
    Feb 1, 2017
    Posts:
    4
    Thank you very much, this one saved my day :)

    I wonder why in UnityWebRequest.Post there is no method overload to send simple binary data and we need this hack:

    Code (CSharp):
    1.  
    2. byte[] bytePostData = Encoding.UTF8.GetBytes(postData);
    3. UnityWebRequest request = UnityWebRequest.Put(url, bytePostData); //use PUT method to send simple stream of bytes
    4. request.method = "POST"; //hack to send POST to server instead of PUT
    5. request.SetRequestHeader("Content-Type", "application/json");
    6.  
    For the sake of knoledge, the code above works with Spring Microservices too.
     
    Last edited: Feb 14, 2018
  12. sumpfkraut

    sumpfkraut

    Joined:
    Jan 18, 2013
    Posts:
    242
    You can use WWWform form, and then form.AddBinaryData() for binary data.
    Then send the form with the UnityWebRequest.
     
  13. enne30

    enne30

    Joined:
    Feb 1, 2017
    Posts:
    4
    I tried that, anyway it did not work with Spring microservice...

    Code (CSharp):
    1.  
    2. byte[] byteArray = Encoding.UTF8.GetBytes(postData);
    3. WWWForm data = new WWWForm();
    4. data.AddBinaryData("body", byteArray);
    5. UnityWebRequest request = UnityWebRequest.Post(url, data);
    6. request.method = "POST";
    7. request.SetRequestHeader("Content-Type", "application/json");
    8.  
    Maybe I have to use something different from "body" in the data.AddBinaryData() ?

    Documentation does not explain parameter fieldName.. https://docs.unity3d.com/ScriptReference/WWWForm.AddBinaryData.html
     
    sdocktor likes this.
  14. sumpfkraut

    sumpfkraut

    Joined:
    Jan 18, 2013
    Posts:
    242
    I use this to upload images to my web server:

    CS:
    Code (CSharp):
    1.     IEnumerator UploadTextureTo(byte[] data, string filename, string md5)
    2.     {
    3.         Debug.Log("Image info: " + filename + ".jpg  |  byte size: " + data.Length.ToString());
    4.         WWWForm form = new WWWForm();
    5.  
    6.         form.AddField("action", "save image");
    7.         form.AddField("sec", md5);
    8.         form.AddField("folder", "MY-FOLDER-NAME");
    9.         form.AddField("filename", filename + ".jpg");
    10.         form.AddField("file", "file");
    11.         form.AddBinaryData("file", data, filename + ".jpg", "images/jpg");
    12.  
    13.         UnityWebRequest uwr = UnityWebRequest.Post("MY-ROOT-URL" + "/" + "MY-PHP-SCRIPT.PHP", form);
    14.         uwr.chunkedTransfer = false; //workaround for unity 2017.3
    15.         yield return uwr.SendWebRequest();
    16.  
    17.         if (uwr.isNetworkError)
    18.         {
    19.             Debug.Log("Error: " + uwr.error);
    20.         }
    21.         else
    22.         {
    23.             if (uwr.uploadProgress == 1 && uwr.isDone)
    24.             {
    25.                 yield return new WaitForSeconds(5);
    26.             }
    27.         }
    28.     }

    PHP-File:
    Code (CSharp):
    1. <?php
    2. header("Access-Control-Allow-Credentials: true");
    3. header('Access-Control-Allow-Origin: *');
    4. header('Access-Control-Allow-Methods: POST, GET, OPTIONS');
    5. header('Access-Control-Allow-Headers: Accept, X-Access-Token, X-Application-Name, X-Request-Sent-Time');
    6.  
    7.     if ($_POST)
    8.     {
    9.         if ( isset ($_POST['action']) && isset ($_POST['folder']) && isset ($_POST['sec']) && $_POST['sec'] === md5($_POST['folder']."_MYSECURESTRING"))
    10.         {
    11.             if($_POST['action'] === 'save image')
    12.             {
    13.                 if(!isset($_FILES) && isset($HTTP_POST_FILES))
    14.                 {
    15.                     $_FILES = $HTTP_POST_FILES;
    16.                 }
    17.                  
    18.                 if ($_FILES['file']['error'] === UPLOAD_ERR_OK)
    19.                 {
    20.                     if ($_FILES['file']['name'] !== "" && isset ($_POST['filename']))
    21.                     {
    22.                         if ($_FILES['file']['type'] === 'images/jpg')
    23.                         {
    24.                             $uploadfile =  $_FILES['file']['name'];
    25.                             $newimage = "Saves/" . $_POST['folder'] . "/" . $_POST['filename'];
    26.                             move_uploaded_file($_FILES['file']['tmp_name'], $newimage);            
    27.                         }
    28.                     }
    29.                 }
    30.             }
    31.         }  
    32.     }
    33. ?>
     
  15. minevr

    minevr

    Joined:
    Mar 4, 2008
    Posts:
    1,018
    Get it~~ request.method = "POST" Thanks!
     
  16. ikust

    ikust

    Joined:
    Sep 2, 2014
    Posts:
    1
    tc_eschenbach likes this.
  17. KennethOAguilar

    KennethOAguilar

    Joined:
    Feb 25, 2015
    Posts:
    6
    If you get 406 Not Acceptable I fixed it doing this.

    var headers = form.headers;

    Dictionary<string, string> ht = new Dictionary<string, string>();
    ht["Accept"] = "*/*";
    ht["Accept-Encoding"] = "gzip, deflate";
    ht["User-Agent"] = "runscope/0.1";
    ht["Content-Type"] = form.headers["Content-Type"].Replace("\"","");

    WWW www = new WWW(url, form.data, ht);

    This is also a workaround to Generic/unknown HTTP error using UnityWebRequest.
     
  18. binoy92

    binoy92

    Joined:
    Mar 19, 2018
    Posts:
    1
    You are the man!
     
  19. Acet

    Acet

    Joined:
    Jun 26, 2014
    Posts:
    13
    I'm using 2017.3.1f1 and just yesterday I implemented some code that uploads JSON data to my server which handles it in PHP.

    It works fine - I don't get any issues (I'm only here because I was googling for using compression to send/receive data). This was tested both in Android and the Unity Editor run mode in x86.

    Here's my code:
    Code (CSharp):
    1.            
    2.             UnityWebRequest sendShowCall = new UnityWebRequest(url);
    3.             sendShowCall.method = UnityWebRequest.kHttpVerbPOST;
    4.             sendShowCall.timeout = userNetworkOpTimeout;
    5.             sendShowCall.uploadHandler = new UploadHandlerRaw (dataToSend);
    6.             sendShowCall.uploadHandler.contentType = "application/json";
    7.  
    Note that dataToSend is simply the JSON string converted to byte[] using System.Text.Encoding.UTF8 and before that a similar version of the code passing string also worked (but all the characters arrived escaped on the server).

    In fact, I have an entirely different version of this code using WWW which I did before whilst investigating this which also works fine (but has no timeout, hence why I use UnityWebRequest here).
     
    Tom42_59 and dan_ginovker like this.
  20. Ursamour

    Ursamour

    Joined:
    Jun 26, 2017
    Posts:
    2
    You are a god. This saved me so much time, thank you.
     
  21. haroldmthyng

    haroldmthyng

    Joined:
    Oct 11, 2018
    Posts:
    1
    I know this is an old thread... but this was the only thing that worked for me. And works like a charm!!!!
     
  22. johnkallen

    johnkallen

    Joined:
    Dec 20, 2015
    Posts:
    2
    For me, I was missing the Download Handler, here is my code (requestData is an Object):

    string jsonString = JsonUtility.ToJson(requestData);
    Debug.Log("jsonString:" + jsonString);
    byte[] bytes = Encoding.UTF8.GetBytes(jsonString);

    using (UnityWebRequest www = new UnityWebRequest (URL)) {
    www.method = UnityWebRequest.kHttpVerbPOST;
    www.uploadHandler = new UploadHandlerRaw (bytes);
    www.downloadHandler = new DownloadHandlerBuffer ();
    www.uploadHandler.contentType = "application/json";
    www.chunkedTransfer = false;

    yield return www.SendWebRequest ();

    if (www.isNetworkError || www.isHttpError) {
    Debug.Log (www.responseCode);
    Debug.Log (www.error);
    } else {
    Debug.Log ("Post complete! RespLength:" + www.downloadHandler.text.Length);
    Debug.Log ("text:" + www.downloadHandler.text);
    }

    }
     
    Deleted User and Tom-Mensink like this.
  23. Sir-Gatlin

    Sir-Gatlin

    Joined:
    Jan 18, 2013
    Posts:
    28
    when dealing with this issue I found this thread first, found a great solution on another thread so I am posting it here to make it more obtainable

    Code (CSharp):
    1. IEnumerator Post(string url, string bodyJsonString)
    2.     {
    3.         var request = new UnityWebRequest(url, "POST");
    4.         byte[] bodyRaw = Encoding.UTF8.GetBytes(bodyJsonString);
    5.         request.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
    6.         request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
    7.         request.SetRequestHeader("Content-Type", "application/json");
    8.         yield return request.SendWebRequest();
    9.         Debug.Log("Status Code: " + request.responseCode);
    10.     }
    Original thread explaining why you have to build it yourself over using build in .post:
    https://forum.unity.com/threads/unitywebrequest-post-url-jsondata-sending-broken-json.414708/
     
    Novack, raja5aurus, Urbanbase and 3 others like this.
  24. Roiw

    Roiw

    Joined:
    Jan 15, 2013
    Posts:
    23
    Thank you so much Sir-Gatlin this helped me a lot!
     
  25. metaphysician

    metaphysician

    Joined:
    May 29, 2012
    Posts:
    190
    hey folks, reviving the old thread. i need to upload bytes to a local webserver via MAMP as PNG files and i've tried a bunch of different methods and still getting errors. it completes with a 404 result. here's the code:

    Code (CSharp):
    1. IEnumerator UploadTextureTo(byte[] data, string filename)
    2.     {
    3.         Debug.Log("Image info: " + filename + "  |  byte size: " + data.Length.ToString());
    4.         WWWForm form = new WWWForm();
    5.         form.AddBinaryData("file", data, filename, "images/png");
    6.         UnityWebRequest uwr = UnityWebRequest.Post("http://localhost:8888/overviews/", form);
    7.         uwr.method = "POST";
    8.         yield return uwr.SendWebRequest();
    9.         if (uwr.isNetworkError)
    10.         {
    11.             Debug.Log("Error: " + uwr.error);
    12.         }
    13.         else
    14.         {
    15.             if (uwr.uploadProgress == 1 && uwr.isDone)
    16.             {
    17.                 yield return new WaitForSeconds(2);
    18.                 Debug.Log("Result:"+uwr.responseCode);
    19.             }
    20.         }
    21.     }
    i tried putting the filename into the URL and it still results with a 404. it's been set to chmod 755 on the server, and i even went and changed the permissions in macOS. my byte[] has already been encoded as PNG before the coroutine is called. if i change the method to PUT i get 405 errors instead.
     
  26. jukka_j

    jukka_j

    Unity Technologies

    Joined:
    May 4, 2018
    Posts:
    953
    When the issue is in the client->server communication, instead of probing at the problem via C# code inside Unity, use the browser's Network tab to diagnose the issue:
    - https://developers.google.com/web/tools/chrome-devtools/network

    If the problem is a HTTP 404, it means the resource is not found. In that case, you'll need to check the server logs to figure out why the server is not serving the file from that URL. Is the URL wrong? Is the file misplaced on the server? Is something else wrong on the request? (server expects authentication, or is (mis)configured to behave in a specific manner with respect to certain request headers?) Server logs can help diagnose if there is something wrong with the server configuration.
     
  27. metaphysician

    metaphysician

    Joined:
    May 29, 2012
    Posts:
    190
    well, i get that, but i'm able to operate a WebRequest that pulls an audio file from a similar folder on the same webserver via an AudioClip. so the location is virtually identical, it's just one is getting, and the other is putting or posting. but i'll check the browser and see if it's somehow a permissions issue for posting still.
     
  28. metaphysician

    metaphysician

    Joined:
    May 29, 2012
    Posts:
    190
    SUCCESS! since there's a paucity of useful information on basic network uploading of binary data as PNG files that isnt 6-8 years old and using obsolete methods, let me share the basic facts (apologies if this is sounding remedial to most of you).

    first if you're using HTTP or UnityWebRequest to upload files to a webserver folder it absolutely needs a way to handle the upload requests at the server destination via a PHP script. this is not pointed out anywhere in the Unity documentation, and isn't intuitive since no such script is required to download the same data. here's an example script that just handles images that i got from a youtube tutorial:

    Code (CSharp):
    1. <?php
    2.  
    3. if (isset($_FILES['uploadFile'])){
    4.  
    5.     $img = $_FILES['uploadFile']['name'];
    6.     $tmpimg = $_FILES['uploadFile']['tmp_name'];
    7.  
    8.     //to get file extension if needed
    9.     //$fileExt = pathinfo($img,PATHINFO_EXTENSION);
    10.  
    11.     move_uploaded_file($tmpimg, "./overviews/$img");
    12.  
    13.     echo "[success] image ($img) uploaded successfully";
    14.     exit();
    15. }
    16. else{
    17.     echo "[error] there is no data with name [myimage]";
    18. }
    19. ?>
    this is saved as index.php on a destination folder. successful uploads are moved into the './overviews' subfolder folder via the move_uploaded_file function. obviously permissions must be appropriately set on the folder. however you may have to configure the Apache webserver's httpd.conf file and make sure it's configured to use PHP. these lines are needed to add or uncomment (may have to be changed to reflect the version/s of PHP you plan to support):

    LoadModule php5_module libexec/apache2/libphp5.so
    AddHandler php5-script php

    finally, there's the Unity uploading script function, usually placed in a Coroutine. here's mine (thanks to the many posters on this thread and others):

    Code (CSharp):
    1. IEnumerator UploadTextureTo(byte[] data, string filename)
    2.     {
    3.         Debug.Log("Image info: " + filename + "  |  byte size: " + data.Length.ToString());
    4.         WWWForm form = new WWWForm();
    5.         form.AddBinaryData("uploadFile", data, filename, "images/png");
    6.         UnityWebRequest uwr = UnityWebRequest.Post("http://localhost:8888/overviews-upload/", form);
    7.         uwr.method = "POST";
    8.         yield return uwr.SendWebRequest();
    9.         if (uwr.isNetworkError)
    10.         {
    11.             Debug.Log("Error: " + uwr.error);
    12.         }
    13.         else
    14.         {
    15.             if (uwr.uploadProgress == 1 && uwr.isDone)
    16.             {
    17.                 yield return new WaitForSeconds(2);
    18.                 Debug.Log("Result:"+uwr.responseCode);
    19.             }
    20.         }
    21.     }
    one last bit of information that may matter - the PHP script declares a generic string name for the upload itself. does not matter what it's called but it think it should match the Form data field (the first string in the form). so mine's called 'uploadFile' and that's declared in the WWWForm as well.

    i hope this helps newbie folks dealing with uploading binary data to webservers. just trying to update the information out there instead of 6-8 year old threads. a lot can change in that time. cheers!
     
    Last edited: Nov 11, 2020
  29. skrpeta

    skrpeta

    Joined:
    Nov 9, 2016
    Posts:
    18
    Thank you :) this trick I used to successfully verify receipt on iOS
     
  30. Alan47

    Alan47

    Joined:
    Mar 5, 2011
    Posts:
    163
    It is unbelievable that Unity forcefully URL-encodes a POST body. I mean... why would anyone do this? I've been working in web backends for several years now and I've yet to encounter an URL encoded request body. Path params, query params? Sure, those come encoded. But the body? This seems extremely weird to me.
     
    aromana likes this.
  31. Hotshot10101

    Hotshot10101

    Joined:
    Jun 23, 2012
    Posts:
    223
    I am having a similar issue. I need to send json data to a server that requires 2 parameters in a
    application/x-www-form-urlencoded format where the second parameter is the json data.

    I am porting an application from Xcode swift to Unity.

    When the xcode app sends the data it is using a POST and the content type is set to application/x-www-form-urlencoded. When the server receives the data here is what it gets:

    Parameters: {"access_token"=>"[FILTERED]", "json_data"=>[{"target"=>"120",

    I cut off the rest of the data because the important part is there at the beginning of the json data for the json_data parameter.

    I have tried several things including ideas from this thread and have my own UploadHandlerRaw and all that. When I look at www.uploadhandler.data and reencode it as a string this is what I get:

    access_token=[FILTERED]&json_data=[{"target":120,

    Looks like it should work, right? Well, when it gets to the server it looks like this:

    access_token=[FILTERED]&json_data="[{\"target\":120,

    It adds a leading double quote (") and slashes to all of the double quotes in the json data. Xcode doesn't do that.

    So it looks like the UnityWebRequest component is url encoding where the xcode isn't even though the xcode code is using the same content-type value that I am using.

    I have tried changing the content-type to application/json, but when I do then server doesn't know it is a form encoded input so it doesn't know how to get the 2 parameters. Only the parameter value for json_data is actually json, so the main content-type has to be x-www-form-urlencoded.

    So how do I get the same behavior that xcode is giving? I need to use x-www-form-urlencoded as the content-type to pass 2 parameters to the POST, but the value of the second parameter needs to be the raw json data without a leading double quote and without the slashes in front of the the double quotes in the json data.

    Here is my current implementation:

    Code (CSharp):
    1.         var raw = Encoding.UTF8.GetBytes($"access_token={AccessToken}&json_data={JSONData}");
    2.  
    3.         UnityWebRequest www = new UnityWebRequest(APIURLPrefix + "/users/add_json_data", "POST");
    4.         www.SetRequestHeader("content-type", "application/x-www-form-urlencoded; charset=utf-8");
    5.         www.uploadHandler = new UploadHandlerRaw(raw);
    6.         www.uploadHandler.contentType = "application/json";
    7.         www.downloadHandler = new DownloadHandlerBuffer();
    8.         yield return www.SendWebRequest();
     
    tiernan-costain likes this.
  32. codemaker2015

    codemaker2015

    Joined:
    Aug 19, 2018
    Posts:
    27
    You might provide a valid JSON string in the request. You should provide double quotes instead of single quotes for each attributes with the help of the escape character ("\").

    Try to change the methods as follows,

    Code (CSharp):
    1.  
    2. public void Start()
    3. {
    4.     string body = "{\"username\":\"" + _username + "\",\"password\",\"" + _password + "\"}"; //important
    5.     StartCoroutine("localhost:3000/login", body));
    6. }
    7. public IEnumerator Login(string url, string bodyJsonString)
    8. {
    9.     UnityWebRequest request = new UnityWebRequest(url, "POST");
    10.     byte[] data = new System.Text.UTF8Encoding().GetBytes(bodyJsonString); // important
    11.     request.uploadHandler = (UploadHandler) new UploadHandlerRaw(data);
    12.     request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
    13.     request.SetRequestHeader("Content-Type", "application/json");  // important
    14.  
    15.     yield return request.SendWebRequest();
    16.  
    17.     if (request.isNetworkError)
    18.         Debug.Log("Error While Sending: " + request.error);
    19.     else
    20.         Debug.Log("Received: " + request.downloadHandler.text);
    21. }
    22.  
     
  33. OleksandrMartysh

    OleksandrMartysh

    Joined:
    Dec 13, 2015
    Posts:
    25
    i used to have similar problem. The backend on the web server was done in a very strange way. I was forced to use such code.


    Code (CSharp):
    1.    // First method (via old WWW)
    2.     IEnumerator Login_v1() {
    3.         // My json-string, written manually or received from any json-parser.
    4.         string postData; // For example   {"username":"MyName","password":"MyPass"}
    5.         // Convert it to byte array!!!
    6.         byte[] bytePostData = Encoding.UTF8.GetBytes(postData);
    7.  
    8.         // Make dictionary to hold some headers.
    9.         Dictionary<string, string> headersDict = new Dictionary<string, string>();
    10.         // Add header to specify json-type data.
    11.         headersDict.Add("Content-Type", "application/json");
    12.  
    13.         // Send WWW request, using byte array and dictionary with headers.
    14.         using (WWW www = new WWW(apiURL, bytePostData, headersDict)) {
    15.             yield return www;
    16.             Debug.Log(www.text);
    17.         }
    18.     }
    Code (CSharp):
    1.     // Second method (via UnityWebRequest)
    2.     IEnumerator Login_v2() {
    3.         // My json-string, written manually or received from any json-parser.
    4.         string postData; // For example   {"username":"MyName","password":"MyPass"}
    5.         // Convert it to byte array!!!
    6.         byte[] bytePostData = Encoding.UTF8.GetBytes(postData);
    7.  
    8.         // The second parameter here will not be used anywhere. You just need to place there something.
    9.         UnityWebRequest request = UnityWebRequest.Post(apiURL, postData);
    10.         // Specify json-type data.
    11.         request.SetRequestHeader("Content-Type", "application/json");
    12.         // Create an uploader for our web request.
    13.         UploadHandlerRaw uploader = new UploadHandlerRaw(bytePostData);
    14.         uploader.contentType = "application/json"; // This line can be omitted if you have already specified it via ' request.SetRequestHeader'
    15.         // Replace default request uploader by new one.
    16.         request.uploadHandler = uploader;
    17.  
    18.         yield return request.SendWebRequest();
    19.         Debug.Log(request.downloadHandler.text);
    20.     }
    So the main idea of these methods are:
    - Pass in the request not a string, and not post-data, and not even WWWForm, but an array of bytes obtained from a json string :)
    - Specify the json data type in the request header

    May be my experience will help somebody to solve their problem.
     
    RRD123 and CheersBro like this.
  34. Hotshot10101

    Hotshot10101

    Joined:
    Jun 23, 2012
    Posts:
    223
    I will have to give this a try.

    Thank you for sharing whether it works for me or not.
     
  35. j9899

    j9899

    Joined:
    Mar 15, 2024
    Posts:
    2
    I tried almost everything but not able to remove 400 error through my code plz help me out in this:
    var jsonObj = JsonUtility.ToJson(newUser);
    Debug.Log($"Json Object {jsonObj}");

    byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonObj);
    UnityWebRequest request = UnityWebRequest.Put(posturi, bodyRaw);
    request.method = "POST";
    request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");
    yield return request.SendWebRequest();
    if (request.result != UnityWebRequest.Result.Success)
    {
    signUpTextWarning.text = request.error;
    Debug.Log(request.error);
    yield break;
    }
    else
    {
    Debug.Log(request.downloadHandler.text);
    }
    Debug.Log("Status Code: " + request.responseCode);
     
  36. j9899

    j9899

    Joined:
    Mar 15, 2024
    Posts:
    2
    This is my code to Post data on my Post API but it's every times gives 400 error and almost every thing I tried to take reference from above code:
    var jsonObj = JsonUtility.ToJson(newUser);
    Debug.Log($"Json Object {jsonObj}");

    byte[] bodyRaw = Encoding.UTF8.GetBytes(jsonObj);
    UnityWebRequest request = UnityWebRequest.Put(posturi, bodyRaw);
    request.method = "POST";
    request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");
    yield return request.SendWebRequest();
    if (request.result != UnityWebRequest.Result.Success)
    {
    signUpTextWarning.text = request.error;
    Debug.Log(request.error);
    yield break;
    }
    else
    {
    Debug.Log(request.downloadHandler.text);
    }
    please help me to resolve this issue.