UnityWebRequest.Post sends strings with escaped characters

I have an API listening outside of Unity. I’m trying to send players score with UnityWebRequest.Post to the API. I am able to send score with Postman but not with Unity. After 2 hours of debugging I finally found what is happening with Wireshark.

What is actually happening here with UnityWebRequest.Post and why and how to fix this problem? Upper packet is sent from Unity, lower is from Postman.


public static IEnumerator SendScore(Score score)
            {
                string bodyData = JObject.FromObject(score).ToString();
                Debug.Log(bodyData); // Check the second image

                using (UnityWebRequest request =
                    UnityWebRequest.Post(GameSettings.ApiPostScoreUrl, bodyData))
                {
                    request.SetRequestHeader("Content-Type", "application/json");
                    yield return request.SendWebRequest();
                
                    if (request.isHttpError || request.isNetworkError)
                    {
                        Debug.LogError("Error on sending the score: " + request.error);
                    }
                    else
                    {
                        Debug.Log("Sent score, possible answer: " + request.downloadHandler.text);
                    }
                }
            }
namespace Api.Models
{
    public class Score {

        public int Points;
        public string Name;
    }
}
1 Like

Anyone? I mean the POST gets sent but my API is unable to map it into a model due to weird characters.

The Unity docs state that:

The Content-Type header will be set to application/x-www-form-urlencoded by default.

Note: Many server backend languages do not properly handle POST requests with Content-Type headers set to encoding others than application/x-www-form-urlencoded or multipart/form-data.

If you’re sure that your API is able to handle unencoded data then you could change the POST headers in Unity to send it unencoded using multipart/form-data as the content-type (here’s an example of posting form data: Unity - Manual: Sending a form to an HTTP server (POST) )

Perhaps this bug is also relevant? https://fogbugz.unity3d.com/default.asp?826626_htlchp13nh8th2to

Hello, thank you for your response.

I wrote my API to use Content-Type application/json. I can verify with Postman that this works (API is also able to parse data into a model correctly, checked it with a breakpoint). I can also verify that with x-www-form-urlencoded it does not work.

In Unity side I am setting Content-Type to be at the line 9:

request.SetRequestHeader("Content-Type", "application/json");

Is there any ways to get around the encoding issue (or escaped characters - I mean those %22 %2a things)?

Normally it doesn’t matter if it’s URL encoded - the API on the server end usually decodes it automatically.

For example, if I send data like that to my PHP code it usually decodes it (or you can decode it when it comes in using the PHP command urldecode).

What language is your API written in? It probably has a function to decode the URL encoded string - might be worth trying to add that to your API.

i’m having exactly the same bug report. Which unity version are you using? 2017.2 seems to work, .3 is broken

I wrote my web API using ASP.NET Core, I have no experience on it and wanted to give it a go. I will read more about URL encoding and ASP.NET Core documentation tomorrow to see if I will find anything. Thank you!

I am using the 2017.2. Can’t remember the first version I used which had the same problem. I had to upgrade to Patch 2017.2.0p4 for fixing another bug (broken WebGL build).

if you get the exact version number that broke it, you can submit a bug report with a test case and they’ll fix it asap, they want to release .3 this week and breaking WWW calls is a critical bug

Simply use Encoding class and convert your JSON string to byte array:

System.Text.Encoding.UTF8.GetBytes(bodyData);

When byte array is passed to UnityWebRequest.Post(), it is sent to server as is.

that’s exactly what im doing already and the server responds with a NULL error. Code works perfectly fine on 2017.2

Can you show that code? Is correct content sent to server?

Hello,

how I can pass byte[ ] to UnityWebRequest.Post()? There is no such constructor as well as uploadHandler.data doesn’t have setter.

EDIT: I get 404 from API with WWWForm… Seems to be incredibly hard with Unity to just send what you want without any escaping or other modifications. Also it seems that WebClient doesn’t work with WebGL build…

 public static IEnumerator SendScore(Score score)
            {
                WWWForm form = new WWWForm();
                form.AddField("Name", score.Name);
                form.AddField("Points", score.Points);

                using (UnityWebRequest request = UnityWebRequest.Post(GameSettings.ApiPostScoreUrl, form))
                {
                    request.SetRequestHeader("Content-Type", "application/json");
                    yield return request.SendWebRequest();
                   
                    if (request.isHttpError || request.isNetworkError)
                    {
                        Debug.LogError("Error on sending the score: " + request.error);
                    }
                    else
                    {
                        Debug.Log("Sent score, possible answer: " + request.downloadHandler.text);
                    }        
                }
            }
var uwr = new UnityWebRequest(url);
uwr.method = "POST";
uwr.uploadHandler = new UploadHandlerRaw(data);
uwr.downloadHandler = new DownloadHandlerBuffer();
3 Likes
public static IEnumerator SendScore(Score score)
            {
                byte[] data = Encoding.UTF8.GetBytes(JObject.FromObject(score).ToString());

                using (UnityWebRequest request = new UnityWebRequest(GameSettings.ApiPostScoreUrl))
                {
                    request.SetRequestHeader("Content-Type", "application/json");
                    request.method = "POST";
                    request.uploadHandler = new UploadHandlerRaw(data);
                    request.downloadHandler = new DownloadHandlerBuffer();

                    yield return request.SendWebRequest();

                    if (request.isNetworkError || request.isHttpError)
                    {
                        Debug.LogError("Unable to send the POST: " + request.error);
                    }
                    else
                    {
                        Debug.Log("Sent the score to the API");
                    }

                }
            }

I get 404 from the API. It seems request.method = “POST” doesn’t work, I get following in the API console:

Any other ways to set the method?

EDIT: It actually works on local (running Unity in the editor as well as having a local version of API and unity connecting to localhost). Problem is with the WebGL build or with my server… I have Linux, running API on http and having a reverse proxy with Apache2 which listens https. Needs more investigating…
EDIT: Removing https didn’t do anything…
EDIT: Ah, found the reason… On another thread:

EDIT: Ok, now everything works… I had API on another host and subdomain. I moved the API now into the same host and subdomain as the game because of deadline, will maybe later research more on this topic. Still using proxy and it works fine. Will try https soon. If someone needs apache conf for such setup then here it is:

Hello there!
I’m afraid that I have the exact same problem with 2017.30f3.

I’m trying to do a simple login, here’s my code :

IEnumerator GetClientAccesToken()
    {
        if(errorCounter >= MAX_ERROR_COUNT)
        {
            StopCoroutine("GetClientAccesToken");
            errorCounter = 0;
            yield break;
        }
        else
        {
            var form = new WWWForm();

            form.AddField("grant_type","password"); 
            form.AddField("client_id","4"); 
            form.AddField("client_secret","kfbhpxTn0HA9Y4Nsf2ewa4JHCeMmaLo7oTNhvjS"); 
            form.AddField("username","desarrollador@guiara.com");
            form.AddField("password","gUIaRA1");

            var headers = form.headers;
            headers["Accept"] = "application/json";

            var rawData = form.data;

            var www = new WWW(_URI_TokenUsuario, rawData, headers);
//            var www = new WWW(_URI_TokenUsuario, form);

            yield return www;

            if(!string.IsNullOrEmpty(www.error)) ///Error en la descarga
            {
                errorCounter ++;
                if(Debug.isDebugBuild)
                {
                    Debug.LogFormat("*** Error at <{0}> with URI <{1}> LOG <{2}>", "GetClientAccesToken", _URI_TokenUsuario, www.text);
                }
                StartCoroutine("GetClientAccesToken");
                yield break;
            }
            else
            {
                try
                {
                    if(Debug.isDebugBuild)
                    {
                        Debug.LogFormat("*** Download succesfull at <{0}> with URI <{1}> LOG <{2}>", "GetClientAccesToken", _URI_TokenUsuario, www.text);
                    }
                       
                    CreateUserToken(www.text);

                    yield break;
                }
                catch (Exception e)
                {
                    Debug.LogFormat("Exception at <{0}>  <{1}>", "GetClientAccesToken", e);
                }
            }
        }
    }

In Unity 2017.2.0f3 my code works out of the box even with this two aproaches:

var www = new WWW(_URI_TokenUsuario, rawData, headers);
var www = new WWW(_URI_TokenUsuario, form);

But in 2017.30f3 it just fails. I’ve the same result with uwr in 2017.3. Ther’s a solution or a “hack” to get arround this?

Thanks in advance.

It’s a serious bug. It is fixed on 2018.1 but not on 2017.3p1 yet. It’s been reported several times, I don’t know why they don’t consider it critical and why it wasn’t fixed before release because it was reported before the cut date.

I’m on the same situation. I can’t update to .3 because then none of my web calls work but i need all the fixes included on .3 so… basically stuck without able to release for weeks

Could we get UT to answer this. How are we to set byte[ ] as a parameter to a method that expects a string? Can you not just create another Post method that doesn’t URL encode the request body?

       //
        // Summary:
        //     Creates a UnityWebRequest configured to send form data to a server via HTTP POST.
        //
        // Parameters:
        //   uri:
        //     The target URI to which form data will be transmitted.
        //
        //   postData:
        //     Form body data. Will be URLEncoded prior to transmission.
        //
        // Returns:
        //     A UnityWebRequest configured to send form data to uri via POST.
        public static UnityWebRequest Post(string uri, string postData);

You can create UploadHandlerRaw, pass byte array to it’s constructor and then assign it to uploadHandler property.

2 Likes

Thank You for the reply.

I can get the UnityWebRequest to work in the editor but not in Android.

Get requests over https work flawlessly, so I just set up a proxy server with a simple state machine. I make requests to the proxy via GET, then depending on the “message” type in the GET URL (POST, PUT, or DELETE) I parse the body from the rest of the data in the GET url and send it to the main server which responds to the proxy which then sends the data back to the app. It’s a pain in the butt, but It works. In the mean time, I am going to keep working on the game it’s self. This is keeping me in a rut and I am not getting anything accomplished. Once I have the game in the Play Store, I will circle back and get the web requests done.

What exactly doesn’t work? Requests over HTTP?
If you use Unity 2018.4 or earlier and android 9 or later, then it is expected, you have to enable clear text traffic in the manifest.
Otherwise, please specify what exactly doesn’t work and check the logcat for error messages.

1 Like