When I started working with Unity, my goal was to create a cross-platform game that could be deployed across multiple platforms: App Store, Google Play, Amazon, Facebook, Web, and Chrome Extension. While this approach sounds great in theory, it requires careful consideration of the libraries you plan to use.
Initially, I decided to use HttpClient based on the System.Net
namespace. However, when compiling to WebGL, I discovered that System.Net
and HttpClient
were excluded from the build, breaking the connection between my game and API.
The solution? Use UnityWebRequest
.
Below are examples of how to build GET and POST requests with JSON using UnityWebRequest.
using System;using System.Collections.Generic;using UnityEngine.Networking;using System.Collections;using Newtonsoft.Json;using UnityEngine;using System.Text;namespace Jumper.Network{public abstract class API{// TODO: add https certificate verification#if UNITY_WEBGL// WebGL version is loaded by browser under https protocol.public const string HOST = "https://api.example.com/client/";#elsepublic const string HOST = "http://api.example.com/client/";#endifpublic static readonly string TOKEN_QUERY_PARAM = "?token=";public static readonly string REGISTER_PATH = "users/register";private const string USER_ID_KEY = ":user_id";#region internal methods for http clientinternal static string path(string path, Dictionary<string, string> properties){// In case the user has no user_id (possibly due to missing internet connection on boot)// we should use the /games namespace for sending requests.if (properties.ContainsKey(USER_ID_KEY) && !string.IsNullOrEmpty(properties[USER_ID_KEY])){return resolve(GAME_PATH + "/:game_id/sessions/:user_id/" + path + TOKEN_QUERY_PARAM + ":token",properties);}return resolve(GAME_PATH + "/:game_id/" + path + TOKEN_QUERY_PARAM + ":token",properties);}internal static string resolve(string path, Dictionary<string, string> properties){foreach(var kv in properties){path = path.Replace(kv.Key, kv.Value);}return path;}internal static string requestError(UnityWebRequest request){string responseBody = string.Empty;if (request.downloadHandler != null){responseBody = request.downloadHandler.text;}return string.Format("[api#error] request status code: {0}, data: ======= response: {1}, error: {2} =======",request.responseCode, responseBody, request.error);}internal static T requestResponse<T>(UnityWebRequest request){try{var responseData = request.downloadHandler.text;return JsonConvert.DeserializeObject<T>(responseData);}catch (Exception ex){Debug.Log(ex.Message);return default(T);}}public const string CONTENT_TYPE_JSON = "application/json";/// <summary>/// Create the instance of authenticated http client./// </summary>/// <returns>The client.</returns>internal static IEnumerator Request(string url, string method, string data = null, Action<UnityWebRequest> done = null){UnityWebRequest request;switch(method){case UnityWebRequest.kHttpVerbGET:request = UnityWebRequest.Get(url);yield return request.SendWebRequest();done?.Invoke(request);break;case UnityWebRequest.kHttpVerbPOST:request = UnityWebRequest.Post(url, data);request.method = UnityWebRequest.kHttpVerbPOST;request.downloadHandler = new DownloadHandlerBuffer();request.uploadHandler = new UploadHandlerRaw(Encoding.UTF8.GetBytes(data));request.SetRequestHeader("Content-Type", CONTENT_TYPE_JSON);request.SetRequestHeader("Accept", CONTENT_TYPE_JSON);yield return request.SendWebRequest();done?.Invoke(request);break;}}internal static IEnumerator Post(string url, object o, Action<UnityWebRequest> done = null) =>Request(url, UnityWebRequest.kHttpVerbPOST, JsonConvert.SerializeObject(o), done);internal static IEnumerator Get(string url, Action<UnityWebRequest> done = null) =>Request(url, UnityWebRequest.kHttpVerbGET, null, done);internal static Action<T1, T2> wrapCallback<T1, T2>(Action<T1, T2> doneCallback){// In case of having a missing done callback, use an empty function// to avoid null checks when invoking the callback.return doneCallback ?? ((_arg1, _arg2) => { });}}#endregion}```C#// Example of using Api.csusing Newtonsoft.Json;using UnityEngine;using System.Collections.Generic;using System;using System.Collections;namespace Jumper.Network{public class Users : API{public static IEnumerator Create(Dictionary<string, string> properties, User user,Action<User, string> doneCallback = null){var done = wrapCallback(doneCallback);try{return Post(path(REGISTER_PATH, properties), user,(request) =>{if (request.isNetworkError || request.responseCode != 201)done(null, requestError(request));elsedone(requestResponse<User>(request), null);});}catch (Exception ex){// Catch all exceptions to ensure the application never crashesDebug.Log(ex.Message);done(null, ex.Message);}return null;}}}```C#// Example from MonoBehaviourStartCoroutine(Users.Create(Api.GameProperties, Api.CurrentUser, (user, err) =>{if (err != null){Debug.LogError(err);}else{// good to go}}));