When I started to work with unity the goal was to have the cross platform game and deploy the game to App Store, Google Play, Amazon, Facebook, Web, Chrome Extension to cover as much as possible platforms. The idea is good but it would require to focus on libraries that you are planning to use.
At the first time I've decided to use HttpClient based on System.Net
namespace but as result of compiling to WebGL System.Net
and HttpClient
was excluded from the build and no connection between game and API.
The solution is to use UnityWebRequest
.
My examples how to build GET, POST requests with JSON.
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/";
#else
public const string HOST = "http://api.example.com/client/";
#endif
public 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 client
internal static string path(string path, Dictionary<string, string> properties)
{
// in çase if user has no user_id because of missing internet connection on boot
// we should use /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 missing done callback use empty function to skip checks
// on null or not callback instance.
return doneCallback ?? ((_arg1, _arg2) => { });
}
}
#endregion
}
// Example of usage Api.cs
using 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));
else
done(requestResponse<User>(request), null);
});
}
catch (Exception ex)
{
// catch here all the exceptions ensure never die
Debug.Log(ex.Message);
done(null, ex.Message);
}
return null;
}
}
}
// Example from MonoBehaviour
StartCoroutine(Users.Create(
Api.GameProperties, Api.CurrentUser, (user, err) =>
{
if (err != null)
{
Debug.LogError(err);
}
else
{
// good to go
}
}));
Top comments (1)
If you want to improve your code, try using promises instead of callbacks, check this library: github.com/proyecto26/RestClient