40

How can I send HTTP GET and POST requests in C# with Unity?

What I want is:

  • send json data in post request (I use Unity serializer, so no need in new one, I just want to pass string in post data and have ability to set ContentType to application/json);
  • get response code and body without any problems;
  • do it all asynchronous without blocking ui rendering.

What I've tried:

  • implementing with HttpWebRequest/HttpWebResponse, but it's too hard and low level (if I won't found anything better, I'll have to use it);
  • using unity WWW, but it doesn't match my requirements;
  • using some external packages from NuGet - Unity don't accept them :(

Most problems were with threading, I'm not experienced enough in it in C#. IDE, I use, is Intellij Rider.

Kamo Spertsian
  • 785
  • 2
  • 8
  • 23
  • 3
    Use [HttpClient](https://msdn.microsoft.com/en-us/library/system.net.http.httpclient(v=vs.118).aspx) – DGibbs Sep 01 '17 at 15:47
  • If you want to do this async, you should use **WWW** and **IEnumerator** that Unity provides, if it's not what you wanted, then write own **HttpClient** with threading, but it's complicated – Markiian Benovskyi Sep 01 '17 at 16:04
  • HTTP Client can be done async in unity. – Sir Oct 24 '17 at 18:43
  • 1
    You can use NuGet packages with Unity, see this Open Source plugin to use promises instead of Coroutines https://github.com/proyecto26/RestClient – jdnichollsc Jan 12 '19 at 09:04

4 Answers4

109

The WWW API should get this done but UnityWebRequest replaced it so I will answer the newer API. It's really simple. You have to use coroutine to do this with Unity's API otherwise you have have to use one of C# standard web request API and Thread. With coroutine you can yield the request until it is done. This will not block the main Thread or prevent other scripts from running.

Note:

For the examples below, if you are using anything below Unity 2017.2, replace SendWebRequest() with Send() and then replace isNetworkError with isError. This will then work for the lower version of Unity. Also, if you need to access the downloaded data in a binary form instead, replace uwr.downloadHandler.text with uwr.downloadHandler.data. Finally, the SetRequestHeader function is used to set the header of the request.

GET request:

void Start()
{
    StartCoroutine(getRequest("http:///www.yoururl.com"));
}

IEnumerator getRequest(string uri)
{
    UnityWebRequest uwr = UnityWebRequest.Get(uri);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError)
    {
        Debug.Log("Error While Sending: " + uwr.error);
    }
    else
    {
        Debug.Log("Received: " + uwr.downloadHandler.text);
    }
}

POST request with Form:

void Start()
{
    StartCoroutine(postRequest("http:///www.yoururl.com"));
}

IEnumerator postRequest(string url)
{
    WWWForm form = new WWWForm();
    form.AddField("myField", "myData");
    form.AddField("Game Name", "Mario Kart");

    UnityWebRequest uwr = UnityWebRequest.Post(url, form);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError)
    {
        Debug.Log("Error While Sending: " + uwr.error);
    }
    else
    {
        Debug.Log("Received: " + uwr.downloadHandler.text);
    }
}

POST request with Json:

 void Start()
 {
     StartCoroutine(postRequest("http:///www.yoururl.com", "your json"));
 }

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

     //Send the request then wait here until it returns
     yield return uwr.SendWebRequest();

     if (uwr.isNetworkError)
     {
         Debug.Log("Error While Sending: " + uwr.error);
     }
     else
     {
         Debug.Log("Received: " + uwr.downloadHandler.text);
     }
 }

POST request with Multipart FormData/Multipart Form File:

void Start()
{
    StartCoroutine(postRequest("http:///www.yoururl.com"));
}

IEnumerator postRequest(string url)
{
    List<IMultipartFormSection> formData = new List<IMultipartFormSection>();
    formData.Add(new MultipartFormDataSection("field1=foo&field2=bar"));
    formData.Add(new MultipartFormFileSection("my file data", "myfile.txt"));

    UnityWebRequest uwr = UnityWebRequest.Post(url, formData);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError)
    {
        Debug.Log("Error While Sending: " + uwr.error);
    }
    else
    {
        Debug.Log("Received: " + uwr.downloadHandler.text);
    }
}

PUT request:

void Start()
{
    StartCoroutine(putRequest("http:///www.yoururl.com"));
}

IEnumerator putRequest(string url)
{
    byte[] dataToPut = System.Text.Encoding.UTF8.GetBytes("Hello, This is a test");
    UnityWebRequest uwr = UnityWebRequest.Put(url, dataToPut);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError)
    {
        Debug.Log("Error While Sending: " + uwr.error);
    }
    else
    {
        Debug.Log("Received: " + uwr.downloadHandler.text);
    }
}

DELETE request:

void Start()
{
    StartCoroutine(deleteRequest("http:///www.yoururl.com"));
}

IEnumerator deleteRequest(string url)
{
    UnityWebRequest uwr = UnityWebRequest.Delete(url);
    yield return uwr.SendWebRequest();

    if (uwr.isNetworkError)
    {
        Debug.Log("Error While Sending: " + uwr.error);
    }
    else
    {
        Debug.Log("Deleted");
    }
}
Nick is tired
  • 6,860
  • 20
  • 39
  • 51
Programmer
  • 121,791
  • 22
  • 236
  • 328
  • Code after "yield return" runs on the main thead, isn't it? – Kamo Spertsian Sep 02 '17 at 14:21
  • I've tried your code, but nothing happens, when I call your method... What am I doing wrong? Here is [my base request class](https://pastebin.com/zSpuXMRD), then [implementation](https://pastebin.com/vmYPbFYT) and call: `new AuthorizationRequest("test124", "Test1234").execute();`. Program doesn't enter in execute method... – Kamo Spertsian Sep 02 '17 at 17:27
  • 3
    Like I said, `UnityWebRequest` is implemented to run in another Thread. coroutine is used to wait/yield for it to finish the request. The yield is imply waiting for `UnityWebRequest` which is **one another Thread** to finish. It does not block the main Thread while yielding. That's why coroutines are awesome. – Programmer Sep 02 '17 at 20:35
  • 2
    My examples shows how to make a http request and also with json. If you copy the json example as it is and paste in your Editor **without** modifying it, it should work. If you modify it and it doesn't work, create a new question. Before you create a new question, I want to tell you the problem I found in your code. `execute` is a coroutine function and should **not** be called directly like you did. It should be called with `StartCoroutine(execute())` function. Take a look at my example again. You must add the `yield return uwr.SendWebRequest();` otherwise you will have issues in the future. – Programmer Sep 02 '17 at 20:40
  • 2
    Finally, the class with the coroutine(`execute`) function **must** inherit from `MonoBehaviour` before you can use `StartCoroutine` function to start the coroutine function. Your class does not inherit from `MonoBehaviour`. If you don't want to inherit from `MonoBehaviour`, see my answer [here](https://stackoverflow.com/a/40509118/3785314) as I described how to do this **without** inheriting from `MonoBehaviour`. Please fix all these before replying to this comment. If you run into a problem, ask a new question but post your code in that question. – Programmer Sep 02 '17 at 20:48
  • @Programmer what if you use `https` with your own cerificate? Unity documentation has literally no information on it =/ – Sir Oct 24 '17 at 04:24
  • @Sir Not with `UnityWebRequest` but you should be able to do that with standard C# API like [HttpClient](https://stackoverflow.com/questions/22251689/make-https-call-using-httpclient). – Programmer Oct 24 '17 at 06:30
  • @Programmer any chance you can include that in the answer, i think https i kinda mandatory these days for a lot of games with login/register stuff. I think a lot of unity dev's don't even realise it and just release non secure stuff. – Sir Oct 24 '17 at 07:04
  • @Sir it's not mandatory unless you are making a bank/billing software. As for just login/register stuff, you can just use OAuth 2 + encryption. Although I've seen many ignored Unity questions about SSL so I will update this answer but you need to show what you have tried first from the link I provided above then the problems you ran into. I will take it from there. You can use pastebin or whatever site you want to host the code you tried then link it here. – Programmer Oct 24 '17 at 07:16
  • The only thing i did not understand is how to get it to accept my certificate from the client. I have https code done but the server rejects the request. But i don't know how i validate my certificate in code theres no examples. – Sir Oct 24 '17 at 18:18
  • @Programmer just want to ask if this is comptible in `Android/IOS`? – Bluetree Dec 28 '17 at 06:12
  • 2
    @Bluetree Yes, it should work on any platform Unity supports. – Programmer Dec 28 '17 at 06:40
  • @Programmer Can I use **POST request with Multipart FormData/Multipart Form File:** to send image saved in `Application.persistentDataPath` and send it to php server? – Bluetree Jan 22 '18 at 09:45
  • 2
    @Bluetree It's right there in my answer. See the `MultipartFormFileSection` under that second. See many of its constructor overload [here](https://docs.unity3d.com/ScriptReference/Networking.MultipartFormFileSection-ctor.html). – Programmer Jan 22 '18 at 10:03
  • It seems they changed constructor, can you update your answer? Seems like the method is now in parameters. and instead of formData have to use some uploadHandler – Sam Tyurenkov Dec 02 '19 at 23:20
2

Use HttpClient and something like:

public static HttpContent DoPost(object payload, string subPath)
    {
        var httpClient = new HttpClient();

        HttpClient.BaseAddress = new Uri(Global.BaseUrl);
        HttpClient.DefaultRequestHeaders.Clear();
        HttpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); // if you're using json service

        // make request
        var response = Global.HttpClient.PostAsJsonAsync(subPath.TrimLeadingSlash(), payload).Result;

        // check for error
        response.EnsureSuccessStatusCode();

        // return result
        return response.Content;
    }

Payload is an object to be serialized to json. If all requests are going to the same baseUrl, you can set up HttpClient globally, and reuse it here

Jonathan
  • 4,916
  • 2
  • 20
  • 37
  • Looks clear. I'll try it. But what about doing it asynchronous? – Kamo Spertsian Sep 01 '17 at 18:40
  • .Result on the end of the request is what makes it synchronous. If you remove that, you can use task / await, etc – Jonathan Sep 01 '17 at 22:33
  • there is no HttpClient class found... And package System.Net.Http doesn't exist. – Kamo Spertsian Sep 02 '17 at 17:39
  • 2
    yes... you can't use HttClient class from Unity in this moment with the current version of the .NET Framework (3.5) – jdnichollsc Oct 22 '17 at 16:35
  • 1
    Unity supports 4.5 if you change the settings in the engine. for unity versions 2017.x and up. – Sir Oct 24 '17 at 19:52
  • What is Payload?? – Olivier Pons Feb 22 '19 at 07:00
  • Most of the time, if you're doing a POST, you're sending some sort of object over the wire to an endpoint. The payload is the object that you're sending. See https://stackoverflow.com/questions/23118249/whats-the-difference-between-request-payload-vs-form-data-as-seen-in-chrome – Jonathan Feb 22 '19 at 15:15
0

https://www.patrykgalach.com/2019/04/18/how-to-call-rest-api-in-unity/

Please refer this link cleanest way to play with data

and do not use www instead of use UnityWebRequest

Omdevsinh Gohil
  • 145
  • 1
  • 4
0

We can use WWW and UnityWebRequest classes to initiate API calls. WWW got obsolete now and Unity recommends using UnityWebRequest over WWW.

void Start() {
    string url = "https://retrofit-backend-demo.herokuapp.com/book";
    StartCoroutine(GetBooksUsingWWW(url));
    StartCoroutine(GetBooksUsingUnityWebRequest(url));
}

IEnumerator GetBooksUsingWWW(string url) {
    using (WWW www = new WWW(url)){
        yield return www;
        Debug.Log(www.text);
        JSONNode jsonNode = JSON.Parse(www.text);
        string title = jsonNode[0]["title"].ToString();
        Debug.Log("Title: " + title);
    }
}

IEnumerator GetBooksUsingUnityWebRequest(string url) {
    UnityWebRequest www = UnityWebRequest.Get(url);
    yield return www.SendWebRequest();

    if(www.isNetworkError || www.isHttpError) {
        Debug.Log(www.error);
    }
    else {
        Debug.Log(www.downloadHandler.text);
        JSONNode jsonNode = JSON.Parse(www.downloadHandler.text);
        string title = jsonNode[0]["title"].ToString();
        Debug.Log("Title: " + title);
    }
}

For demo: https://github.com/codemaker2015/api-interaction-unity3d-demo

Codemaker2015
  • 12,190
  • 6
  • 97
  • 81