Files
DP44/DataPRO/CanFDApiProxy/RESTWrapper.cs
2026-04-17 14:55:32 -04:00

227 lines
9.4 KiB
C#

using CANFDApiProxy.Messages;
using Newtonsoft.Json;
using System;
using System.CodeDom;
using System.ComponentModel;
using System.IO;
using System.Net.Http;
using System.Reflection;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace CANFDApiProxy
{
internal static class RESTWrapper
{
/// <summary>
/// Get the description part of the enum
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="source"></param>
/// <returns></returns>
private static string DescriptionAttr<T>(this T source)
{
FieldInfo fi = source.GetType().GetField(source.ToString());
DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes(
typeof(DescriptionAttribute), false);
if (attributes != null && attributes.Length > 0) return attributes[0].Description;
else return source.ToString();
}
public static int Port { get; set; } = 5000;
private static readonly TimeSpan TIMEOUT_DEFAULT = TimeSpan.FromSeconds(30);
public static TimeSpan Timeout { get; set; } = TIMEOUT_DEFAULT;
public static Protocol Protocol { get; set; } = Protocol.http;
/// <summary>
///
/// </summary>
/// <param name="host">device ip address or host name</param>
/// <param name="command"></param>
/// <param name="cancellationToken"></param>
/// <returns></returns>
/// <exception cref="ArgumentNullException"></exception>
public static async Task<string> GetResourceAsync(string host, CommandName command, CancellationToken cancellationToken)
{
VerifyHost(host);
try
{
var httpClient = HttpClientFactory.CreateHttpClient();
httpClient.Timeout = Timeout;
var commandString = command.DescriptionAttr();
string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}";
var response = await httpClient.GetAsync(requestUri, cancellationToken);
if (!response.IsSuccessStatusCode)
{
if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError)
{
var errorJson = await response.Content.ReadAsStringAsync();
var res = JsonConvert.DeserializeObject<ErrorMessage>(errorJson);
throw new CanApiException(res.Error, 500);
}
response.EnsureSuccessStatusCode();
}
return await response.Content.ReadAsStringAsync();
}
catch (TaskCanceledException ex)
{
throw new CanApiException("An API call was cancelled or timedout", ex);
}
}
public static async Task<string> PostResourceAsync<T>(string host, CommandName command, T request, CancellationToken cancellationToken)
{
VerifyHost(host);
try
{
HttpResponseMessage response = await PostAsJsonAsync(host, command, request, Timeout, cancellationToken);
return await response.Content.ReadAsStringAsync();
}
catch (TaskCanceledException ex)
{
throw new CanApiException("An API call was cancelled or timedout", ex);
}
}
public static async Task<Stream> SendResourceReadAsStreamAsync<T>(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken)
{
VerifyHost(host);
return await SendResourceReadAsStreamProcess(host, command, request, timeOut, cancellationToken);
}
private static async Task<Stream> SendResourceReadAsStreamProcess<T>(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken)
{
try
{
HttpResponseMessage response = await SendAsync(host, command, request, timeOut, cancellationToken);
return await response.Content.ReadAsStreamAsync();
}
catch (TaskCanceledException ex)
{
throw new CanApiException("An API call was cancelled or timedout", ex);
}
}
private static void VerifyHost(string host)
{
if (string.IsNullOrEmpty(host)) { throw new ArgumentNullException("host", "host cannot be empty"); }
}
public static async Task<string> PostResourceReadAsStringAsync(string host, CommandName command, MultipartFormDataContent multipart, TimeSpan timeOut, CancellationToken cancellationToken)
{
VerifyHost(host);
try
{
var response = await PostWithMultipartFormAsync(host, command, multipart, timeOut, cancellationToken);
return await response.Content.ReadAsStringAsync();
}
catch (TaskCanceledException ex)
{
throw new CanApiException("An API call was cancelled or timedout", ex);
}
}
private static async Task<HttpResponseMessage> PostAsJsonAsync<T>(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken)
{
var httpClient = HttpClientFactory.CreateHttpClient();
httpClient.Timeout = timeOut;
var commandString = command.DescriptionAttr();
string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}";
var response = await httpClient.PostAsJsonAsync(requestUri, request, cancellationToken);
await ValidateResponse(response);
return response;
}
private static async Task<HttpResponseMessage> PostWithMultipartFormAsync(string host, CommandName command, MultipartFormDataContent multipart, TimeSpan timeOut, CancellationToken cancellationToken)
{
var httpClient = HttpClientFactory.CreateHttpClient();
httpClient.Timeout = timeOut;
var commandString = command.DescriptionAttr();
string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}";
var response = await httpClient.PostAsync(requestUri, multipart, cancellationToken);
await ValidateResponse(response);
return response;
}
private static async Task<HttpResponseMessage> SendAsync<T>(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken)
{
var httpClient = HttpClientFactory.CreateHttpClient();
httpClient.Timeout = timeOut;
var commandString = command.DescriptionAttr();
string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}";
HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri);
var json = JsonConvert.SerializeObject(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
httpRequestMessage.Content = content;
var response = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
await ValidateResponse(response);
return response;
}
private static async Task<ErrorMessage> BuildErrorMessage(HttpResponseMessage response)
{
var errorJson = await response.Content.ReadAsStringAsync();
var res = JsonConvert.DeserializeObject<ErrorMessage>(errorJson);
return res;
}
private static async Task ValidateResponse(HttpResponseMessage response)
{
if (!response.IsSuccessStatusCode)
{
switch (response.StatusCode)
{
case System.Net.HttpStatusCode.BadRequest:
throw new CanApiException((await BuildErrorMessage(response)).Error, 400);
case System.Net.HttpStatusCode.Forbidden:
throw new CanApiException((await BuildErrorMessage(response)).Error, 403);
case System.Net.HttpStatusCode.NotFound:
throw new CanApiException((await BuildErrorMessage(response)).Error, 404);
case System.Net.HttpStatusCode.InternalServerError:
throw new CanApiException((await BuildErrorMessage(response)).Error, 500);
case System.Net.HttpStatusCode.ServiceUnavailable:
throw new CanApiException((await BuildErrorMessage(response)).Error, 503);
}
response.EnsureSuccessStatusCode();
}
}
public static async Task WriteStreamToFileAsync(Stream inputStream, string filePath)
{
if (inputStream.CanSeek)
{
inputStream.Position = 0;
}
using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write))
{
await inputStream.CopyToAsync(fileStream);
}
}
}
}