Unity REST API interaction with UnityWebRequest and BestHTTP

Previously I discussed how to perform OAuth 1.0a authentication in C#, which is necessary when communicating RESTfully with WooCommerce using Unity. Now I would like to get into more depth in terms of how to do the actual communication with the API using Representational State Transfer (REST) in Unity itself. Note that Unity 5.5 was used when writing this post.

 Creating an OAuth signed URL

Since I want to communicate with my WooCommerce REST API, I have to use OAuth 1.0a, which I implemented myself (called OAuth_CSharp library, which can be found here). The OAuth_CSharp library has a public method called GenerateRequestURL(), which takes the URL as a string, the HTTP method (e.g. GET or POST), and optionally a list of custom user parameters. It then returns the OAuth signed URL as a string.

Furthermore, to simplify the usage of the library, I wrote an additional helper function that I attached to my GameObject in question. It takes the URL, parameters and optionally the HTTP method (by default it’s “GET”), and returns the signed URL. This function is as follows:

string GenerateRequestURL(string in_url, List<string> paramaters, string HTTP_Method = "GET")
{
    OAuth_CSharp oauth = new OAuth_CSharp(oauth_consumerKey, oauth_consumerSecret);
    string requestURL = oauth.GenerateRequestURL(in_url, HTTP_Method, paramaters);

    return requestURL;
}

Ok, now that we can generate our signed URL, lets use it. For this I have two implementations. First using Unity’s built in UnityWebRequest and secondly using BestHTTP, which I suppose in some sense has been made obsolete for REST interactions by UnityWebRequest, but since I own it, I will explain it as well.

UnityWebRequest

With the release of Unity 5.3, the Android and iOS backed for UnityWebRequest was finished, allowing easy RESTful interactions built into Unity. To use it simply add using UnityEngine.Networking to the top of your C# file.

Two of the main REST functions you would want to use is GET and POST. Therefore, here are the two basic implementations of each.

GET

It is very easy to perform a GET request, and the best way to do it is using a coroutine. In it’s most basic form the code would look something like this:

//in main thread
StartCoroutine(GetRequest(uri));

...

IEnumerator GetRequest(string uri)
{
    UnityWebRequest request = UnityWebRequest.Get(uri);        
    yield return request.Send();
    
    // Show results as text        
    Debug.Log(request.downloadHandler.text);
}

However, I would like to have some validation and since this request needs to be processed further in the program, I created two flags: requestFinished and requestErrorOccurred, which are initially set to false. Furthermore, I would like to process the results further (which will be discussed later). We will also now include the OAuth signing of the URL. A point to note, in my experience I also sometimes inexplicably ran into a 401 error, indicating my signature was not valid. However, if I generate a new signature this problem is almost always solved. So I would also like to automatically resend the request if this happens (you could also possibly build in a counter, then make sure this is not an infinite loop). To do this I created two variables lastRequestURL and lastRequestParameters, which simply contains the URL and parameters.

So the new complete code looks like this. If GET was successful a status of 200 will be returned:

//in main thread
StartCoroutine(GetRequest(GenerateRequestURL(lastRequestURL, lastRequestParameters)));

...

IEnumerator GetRequest(string uri)
{
    requestFinished = false;
    requestErrorOccurred = false;

    UnityWebRequest request = UnityWebRequest.Get(uri);
    yield return request.Send();

    requestFinished = true;
    if (request.isError)
    {
        Debug.Log("Something went wrong, and returned error: " + request.error);
        requestErrorOccurred = true;
    }
    else
    {
        // Show results as text
        Debug.Log(request.downloadHandler.text);

        if (request.responseCode == 200)
        {
            Debug.Log("Request finished successfully!");
        }
        else if (request.responseCode == 401) // an occasional unauthorized error
        {
            Debug.Log("Error 401: Unauthorized. Resubmitted request!");
            StartCoroutine(GetRequest(GenerateRequestURL(lastRequestURL, lastRequestParameters)));
            requestErrorOccurred = true;
        }
        else
        {
            Debug.Log("Request failed (status:" + request.responseCode + ")");
            requestErrorOccurred = true;
        }

        if (!requestErrorOccurred)
        {
            yield return null;
            // process results
        }
    }
}

The request.downloadHandler.text contains the JSON response text from the server, which we will deserialize into a useable object soon.

POST

For some reason posting is a bit more challenging with UnityWebRequest. You ought to be able to use it with WWWForm using this example on the Unity docs, but it could be that I’m doing something wrong, but I couldn’t manage to get it to work. With the help of these forums (here and here), I managed to get it to work the hard way using the following code segment:

//in main thread
StartCoroutine(PostRequest(url, JSON_Body));

...

IEnumerator PostRequest(string url, string bodyJsonString)
{
    var request = new UnityWebRequest(url, "POST");
    byte[] bodyRaw = new System.Text.UTF8Encoding().GetBytes(bodyJsonString);
    request.uploadHandler = (UploadHandler) new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = (DownloadHandler) new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");

    yield return request.Send();

    Debug.Log("Response: " + request.downloadHandler.text);
}

I’m not going to explain much of this, you have the Unity docs for that, but this is all the necessary code to send a request to the server. The response will be in the DownloadHandler object attached to the UnityWebRequest object. And again the response will be a JSON string which can be deserialized and processed further.

Similar to the GET case, here is the full code with validation and output. If POST was successful a status of 201 will be returned:

//in main thread
StartCoroutine(PostRequest(GenerateRequestURL(lastRequestURL, lastRequestParameters, "POST"), JSON_body));

...

IEnumerator PostRequest(string url, string bodyJsonString)
{
    requestFinished = false;
    requestErrorOccurred = false;

    var request = new UnityWebRequest(url, "POST");
    byte[] bodyRaw = new System.Text.UTF8Encoding().GetBytes(bodyJsonString);
    request.uploadHandler = (UploadHandler)new UploadHandlerRaw(bodyRaw);
    request.downloadHandler = (DownloadHandler)new DownloadHandlerBuffer();
    request.SetRequestHeader("Content-Type", "application/json");

    yield return request.Send();
    requestFinished = true;

    if (request.isError)
    {
        Debug.Log("Something went wrong, and returned error: " + request.error);
        requestErrorOccurred = true;
    }
    else
    {
        Debug.Log("Response: " + request.downloadHandler.text);

        if (request.responseCode == 201)
        {
            Debug.Log("Request finished successfully! New User created successfully.");
        }
        else if (request.responseCode == 401)
        {
            Debug.Log("Error 401: Unauthorized. Resubmitted request!");
            StartCoroutine(PostRequest(GenerateRequestURL(lastRequestURL, lastRequestParameters, "POST"), bodyJsonString));
            requestErrorOccurred = true;
        }
        else
        {
            Debug.Log("Request failed (status:" + request.responseCode + ").");
            requestErrorOccurred = true;
        }

        if (!requestErrorOccurred)
        {
            yield return null;
            // process results
        }
    }
}

Best HTTP

Before the days of UnityWebRequest, it was a bit more messy to do REST server request with Unity’s WWW. Best HTTP was a great help to make these requests much simpler. It also has many, many other features which might make it a worthwhile plugin to consider. Especially when dealing with cookies. The plugin is however not cheap with a $60 price tag at time of writing. Here is a brief look at implementing the same GET and POST request using Best HTTP.

GET

Similar to UnityWebRequest this is very simple to do. Just use HTTPRequest and supply the callback function. Here is the example from the included documentation: 

//in main thread
HTTPRequest request = new HTTPRequest(new Uri("https://google.com"), onRequestFinished);
request.Send();

...

void OnRequestFinished(HTTPRequest request, HTTPResponse response)
{
    Debug.Log("Request Finished! Text received: " + response.DataAsText);
}

And once again the response.DataAsText is a JSON string that we will deserialize later.

POST

This is also very simple, so again the example from the documentation. This time you have to specify the HTTP method.

//in main thread
HTTPRequest request = new HTTPRequest(new Uri("http://server.com/path"), HTTPMethods.Post,
OnRequestFinished);
request.AddHeader("Content-Type", "application/json");
request.RawData = new System.Text.UTF8Encoding().GetBytes(JSON_body);
request.Send();

...

void OnRequestFinished(HTTPRequest request, HTTPResponse response)
{
    Debug.Log("Request Finished! Text received: " + response.DataAsText);
}

Similar to the UnityWebRequest examples the Best HTTP GET and POST callback functions can be customized to include all the necessary validation and output generation.

Serializing/Deserializing a JSON string

Deserializing simply means you take the JSON response which is difficult to process in C# and convert it into a simple easy to use representation. It is really up to you how you want to do it. A free approach would be to use SimpleJSON, however a much more elegant approach would be to use the JSON .NET plugin for Unity. It is $25 but in terms of time saving and simplicity, you can’t go wrong.

The easies way to set everything up is to create a request, output it to the console, and copy the entire response and paste it into json2csharp. You can then generate the class that JSON .NET requires for serializing/deserializing your JSON text. I would suggest creating a new class, for example Products, and then copy and paste all the sub classes generated by json2csharp into that class. You can then deserialize your JSON string with the following line of code: 

Product.RootObject product = JsonConvert.DeserializeObject(JSON_string);

Later when you need to serialze an object into a JSON string to POST it to the server you would simply do it with the following line of code:

string JSON_body = JsonConvert.SerializeObject(newProduct);

where newProduct is of type Product.RootObject. Remember that before you can POST it, the JSON string must first be encoded into UTF8 format, as is done in the examples above.

And that’s it! You can now GET and POST JSON strings to a server using Unity.

8 thoughts on “Unity REST API interaction with UnityWebRequest and BestHTTP

  1. The issue with POST and UnityWebRequest is what UnityWebRequest does to the JSON string before sending it. I don’t know why but it tries to escape certain characters. For example this { “test”: 1} turns into something like this %7b+%22test%22%3a1%7d. You can see this by using fiddler to check the network traffic. That is why you need to use System.Text.UTF8Encoding().GetBytes(bodyJsonString);

    Liked by 1 person

  2. Nice post, your OAuth_CSharp class helped greatly in getting a Unity application to communicate with an integrated Slack application! Just thought you should know this makes Slack integration really super easy within the varying Unity supported .Net environments.
    For anyone else interested in more information on that, you will have to setup your own Slack app to get the ‘consumer key’ and the ‘secret key’ for the slack app OAuth2.0 settings – then using OAuth_CSharp, you generate your url string. I then built a simple Serializable class with a public string variable called ‘text’ – use Unity’s JSonUtility to serialize that as the Json payload, sent it up and it worked great!

    Thanks again!

    Like

Leave a comment