147 lines
4.9 KiB
C#
147 lines
4.9 KiB
C#
using System;
|
|
using System.IO;
|
|
using System.Text;
|
|
using Best.HTTP;
|
|
using Cysharp.Threading.Tasks;
|
|
using IchniOnline.Online.Network.Models;
|
|
using UnityEngine;
|
|
|
|
namespace IchniOnline.Online.Network
|
|
{
|
|
/// <summary>
|
|
/// BestHTTP-based API client singleton for IchniOnline backend communication.
|
|
/// Pure HTTP layer — no business logic.
|
|
/// </summary>
|
|
public class IchniOnlineApiClient
|
|
{
|
|
private static IchniOnlineApiClient _instance;
|
|
public static IchniOnlineApiClient Instance => _instance ??= new IchniOnlineApiClient();
|
|
|
|
public string BaseUrl { get; set; } = "http://localhost:5308";
|
|
public string JwtToken { get; set; }
|
|
|
|
private IchniOnlineApiClient() { }
|
|
|
|
public async UniTask<ApiResult<T>> GetAsync<T>(string endpoint)
|
|
{
|
|
string url = BuildUrl(endpoint);
|
|
var request = new HTTPRequest(new Uri(url), HTTPMethods.Get);
|
|
AddAuthHeader(request);
|
|
|
|
try
|
|
{
|
|
var resp = await SendAsync(request);
|
|
return ProcessResponse<T>(resp);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return ApiResult<T>.Fail(ResponseCode.InternalServerError, "Network error", ex.Message);
|
|
}
|
|
}
|
|
|
|
public async UniTask<ApiResult<T>> PostAsync<T>(string endpoint, object body)
|
|
{
|
|
string url = BuildUrl(endpoint);
|
|
var request = new HTTPRequest(new Uri(url), HTTPMethods.Post);
|
|
request.SetHeader("Content-Type", "application/json");
|
|
AddAuthHeader(request);
|
|
|
|
if (body != null)
|
|
{
|
|
string jsonBody = JsonUtility.ToJson(body);
|
|
request.UploadSettings.UploadStream = new MemoryStream(Encoding.UTF8.GetBytes(jsonBody));
|
|
}
|
|
|
|
try
|
|
{
|
|
var resp = await SendAsync(request);
|
|
return ProcessResponse<T>(resp);
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return ApiResult<T>.Fail(ResponseCode.InternalServerError, "Network error", ex.Message);
|
|
}
|
|
}
|
|
|
|
private string BuildUrl(string endpoint)
|
|
{
|
|
if (string.IsNullOrEmpty(BaseUrl))
|
|
throw new InvalidOperationException("BaseUrl is not configured.");
|
|
|
|
string baseUrl = BaseUrl.TrimEnd('/');
|
|
string path = endpoint.StartsWith("/") ? endpoint : $"/{endpoint}";
|
|
return baseUrl + path;
|
|
}
|
|
|
|
private void AddAuthHeader(HTTPRequest request)
|
|
{
|
|
if (!string.IsNullOrEmpty(JwtToken))
|
|
{
|
|
request.SetHeader("Authorization", $"Bearer {JwtToken}");
|
|
}
|
|
}
|
|
|
|
private UniTask<HTTPResponse> SendAsync(HTTPRequest request)
|
|
{
|
|
var completionSource = new UniTaskCompletionSource<HTTPResponse>();
|
|
|
|
request.Callback = (req, resp) =>
|
|
{
|
|
switch (req.State)
|
|
{
|
|
case HTTPRequestStates.Finished:
|
|
completionSource.TrySetResult(resp);
|
|
break;
|
|
case HTTPRequestStates.Aborted:
|
|
completionSource.TrySetCanceled();
|
|
break;
|
|
default:
|
|
completionSource.TrySetException(req.Exception ?? new Exception($"HTTP request failed: {req.State}"));
|
|
break;
|
|
}
|
|
};
|
|
|
|
request.Send();
|
|
return completionSource.Task;
|
|
}
|
|
|
|
private ApiResult<T> ProcessResponse<T>(HTTPResponse resp)
|
|
{
|
|
string json = resp.DataAsText;
|
|
|
|
if (resp.StatusCode >= 200 && resp.StatusCode < 300)
|
|
{
|
|
if (string.IsNullOrEmpty(json))
|
|
{
|
|
return ApiResult<T>.Fail(ResponseCode.InternalServerError, "Empty response body");
|
|
}
|
|
|
|
var response = JsonUtility.FromJson(json, typeof(GlobalResponse<T>)) as GlobalResponse<T>;
|
|
if (response == null)
|
|
{
|
|
return ApiResult<T>.Fail(ResponseCode.InternalServerError, "Failed to parse response JSON");
|
|
}
|
|
|
|
if (response.code == ResponseCode.Ok)
|
|
{
|
|
return ApiResult<T>.Ok(response.data);
|
|
}
|
|
|
|
return ApiResult<T>.Fail(response.code, response.message);
|
|
}
|
|
|
|
// Non-2xx: try to parse server error body
|
|
if (!string.IsNullOrEmpty(json))
|
|
{
|
|
var errorResponse = JsonUtility.FromJson(json, typeof(GlobalResponseBase)) as GlobalResponseBase;
|
|
if (errorResponse != null)
|
|
{
|
|
return ApiResult<T>.Fail(errorResponse.code, errorResponse.message);
|
|
}
|
|
}
|
|
|
|
return ApiResult<T>.Fail(ResponseCode.InternalServerError, $"HTTP error {resp.StatusCode}");
|
|
}
|
|
}
|
|
}
|