Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generic/Unknown HTTP Error with response code 0 using UnityWebRequest

I was trying to use some server's API functions, and suddenly came to this error. I will explain it after the code sample:

public IEnumerator Post(Dictionary<string, string> data)
    {
        var request = UnityWebRequest.Post(Url, data);
        request.SetRequestHeader("Content-Type", "application/x-www-form-urlencoded");

        yield return request.Send();

        if (request.isError)
           UpdateNetworkLog("Network error has occured: " + request.error);
        else
            // Some code after success
    }

As you can see, mostly I took it from the Unity manual considering POST requests here. But, there is one problem - the request never finished correctly, it always has error (which is shown using UpdateNetworkLog function): "Generic/unknown HTTP error", while request's response code is 0. Also, I've tried to use legacy method from this manual and WWW class getting the same result.

I think, problem is in how Unity gets the response: I have checked packets via Wireshark, both my POST request and server's response are perfectly fine, no errors or mistakes.

Another important issue is that I'm using WebGL, and everything's all right in Editor, program gets the proper response and works correctly; error appears only after I build and run the game in the browser (tested in Chrome and Safari). Also, tried launch it using not the Unity WebGL Player, but XAMPP: the same.

Thanks for all the future responses, as Google does not know anything about it.

like image 961
Pavel Kotov Avatar asked Jun 23 '17 14:06

Pavel Kotov


3 Answers

Potential Solution 1

From the Unity documentation on using UnityWebRequest with WebGL:

The WWW and UnityWebRequest classes are supported in WebGL. They are implemented using the XMLHttpRequest class in JavaScript, using the browser to handle WWW requests. This imposes some security restrictions on accessing cross-domain resources. Basically any WWW request to a server which is different from the server hosting the WebGL content needs to be authorized by the server you are trying to access. To access cross-domain WWW resources in WebGL, the server you are trying to access needs to authorize this using CORS.

My best guess is that this server that you are trying to access does not have CORS correctly set up or configured. When your code is running in a browser, the browser itself is handling the request and consequently subjugating your code to that browser's security restrictions. Your program and the server aren't on the same domain, and the browser doesn't like that.

If this is your problem, then without more information it's difficult for me to suggest a solution. From your post, it sounds like you might be relatively new to coding (or at least to HTTP-related coding), and the best solutions I can find to your problem involve some relatively complex methods, such as using JavaScript instead of UnityWebRequest.

Potential Solution 2

Of course, the actual problem might even lie with your surrounding code instead, or with the server you're trying to access. What is your code for the function that calls Post()? There could be some issue with order-of-events that's showing up only when the code is run in a browser (where JavaScript is handling the POST request).

On a side note, if the server you're trying to access has documentation for their API, your problem might be addressed somewhere in it.

like image 200
Allison Meek Avatar answered Nov 10 '22 08:11

Allison Meek


The first thing to point out is that UnityWebRequest actually returns the response in

request.downloadHandler.text

While only returning the general error information in

request.error


Now if your case is like mine and you upgraded Unity only to have all your networking code break, the error you'll likely see is related to Content-Length

This is because for both the WWW and UnityWebRequest class now have chunkedTransfer on by default. This appears to screw up how content length is sent over and causes the error.

While chunkedTransfer can currently be disabled for UnityWebRequest, it cannot be disabled for the WWW class at the time of this writing. For me this meant rewriting all my old network code to use UnityWebRequest instead and set

myUnityWebRequest.chunkedTransfer = false;

Note: UnityWebRequest does not currently support POSTs with no data. You still need to use the WWW class for this.

like image 35
Maxx Avatar answered Nov 10 '22 08:11

Maxx


This problem is very common. If you have worked with Rest API then things are pretty easy to understand.

Enable CORS:

I have created a rest API using python flask and it was hosted in Heroku. The advantage of Heroku was it support CORS by default and you do not need to set it up. Nowadays maximum server where you host your app especially python has the same facility. So those who have CORS related problem they have to first enable CROS it is mandatory.

Test In Postman:

It is required to test your API in Postman. If your app does not support CORS still it will work in Postman. So working in postman does not ensure that it will run in Unity. The next important thing to consider is the Headers that you are using in the postman.

Like : {"Key":"Content-Type" , "value":"application/json" }

because you also need to pass it in unity www header.

The next thing needs to observe is what you are passing in the body and how you are passing it. Normally we send it as JSON(application/json) in Postman.

Like:

`{
    "username":"Mukesh",
    "password":"admin@1234",
    "email_id":"[email protected]",
    "us_dollar":"2000000000",
    "device_id":"123456789"

}`

Now if your CORS enabled API is working fine in Postman then just relax it will also work in unity.

Unity Part :

First I have to create a class that will convert into Json.

[Serializable]
public class UserRegistration
{
    //CAUTION:
    //NOTE:
    //DO NOT ALTER THE NAME OF PARAMETERS .
    //THE JSON key WILL FAIL THEN .Properties will be the key of JSON .
    /// <summary>
    /// User name .
    /// </summary>
    public  string username;
    /// <summary>
    /// Password .
    /// </summary>
    public string password;
    /// <summary>
    /// Emil id.
    /// </summary>
    public string email_id;
    /// <summary>
    /// JoeGames Doller .
    /// </summary>
    public string us_dollar;
    /// <summary>
    /// Device id .
    /// </summary>
    public  string device_id;

    /// <summary>
    /// Constructor .
    /// </summary>
    /// <param name="username"></param>
    /// <param name="password"></param>
    /// <param name="email_id"></param>
    /// <param name="us_dollar"></param>
    /// <param name="device_id"></param>
    public UserRegistration(string username, string password, string email_id, string us_dollar, string device_id)
    {
        this.username = username;
        this.password = password;
        this.email_id = email_id;
        this.us_dollar = us_dollar;
        this.device_id = device_id;
    }

}

Next thing you have to convert the object in json.

//var create json.
    var userregistration = new UserRegistration(
        username: username,
        password: password,
        email_id: emailid,
        us_dollar: joeGamesDollar.ToString(),
        device_id: deviceid);
    //convert to json.
    var json = JsonUtility.ToJson(userregistration);
    //debug.
    Debug.Log("Print json"+json);
    //update json.
    json = json.Replace("'", "\"");

Next, convert json into the byte and make the post with the header.

//Encode the JSON string into a bytes
    var postData = System.Text.Encoding.UTF8.GetBytes(json);
    //create headers.
    //Add keys an values .
    var headers = new Dictionary<string, string> {{"Content-Type", "application/json"}};
    //Now call a new WWW request
    var www = new WWW(url,postData,headers);
    //Yield return www .
    yield return www;

    Debug.Log("Error" + www.error.ToString());
    Debug.Log("Data" + www.text);
    Debug.Log(www.responseHeaders+"Done"+www.isDone);
    //dispose.
    www.Dispose();

Do not forget to write it in an Enumerator. If you use something

like:

IEnumerator Upload()
    {
        WWWForm form = new WWWForm();
        form.AddField("username", "Mukesh");
        form.AddField("password", "admin@1234");
        form.AddField("email_id", "[email protected]");
        form.AddField("us_dollar", "2000000");
        form.AddField("device_id", device_id);


        using (UnityWebRequest www = UnityWebRequest.Post(url, form))
        {
            yield return www.SendWebRequest();

            if (www.isNetworkError || www.isHttpError)
            {
                Debug.Log(www.error);
            }
            else
            {
                Debug.Log("Form upload complete!");
            }
        }
    } 

You will get a Generic / Unknown Error because Unity Web Request can not make the post or send the data as your API required in this circumstance.

NOTE: Some time 400 Bad request is also served as an unknown Generic/Unknown HTTP Error in unity. So do not forget to check the code in the backend to make sure your API is not getting any exception .

like image 27
Reezoo Avatar answered Nov 10 '22 06:11

Reezoo