namespace Swan.Net { using Formatters; using System; using System.Collections.Generic; using System.Net.Http; using System.Net.Http.Headers; using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; /// /// Represents a HttpClient with extended methods to use with JSON payloads /// and bearer tokens authentication. /// public static class JsonClient { private const string JsonMimeType = "application/json"; private const string FormType = "application/x-www-form-urlencoded"; private static readonly HttpClient HttpClient = new HttpClient(); /// /// Post a object as JSON with optional authorization token. /// /// The type of response object. /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested type. /// public static async Task Post( Uri requestUri, object payload, string? authorization = null, CancellationToken cancellationToken = default) { var jsonString = await PostString(requestUri, payload, authorization, cancellationToken) .ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Posts the specified URL. /// /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result as a collection of key/value pairs. /// public static async Task?> Post( Uri requestUri, object payload, string? authorization = null, CancellationToken cancellationToken = default) { var jsonString = await PostString(requestUri, payload, authorization, cancellationToken) .ConfigureAwait(false); return string.IsNullOrWhiteSpace(jsonString) ? default : Json.Deserialize(jsonString) as IDictionary; } /// /// Posts the specified URL. /// /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// url. /// Error POST JSON. public static Task PostString( Uri requestUri, object payload, string? authorization = null, CancellationToken cancellationToken = default) => SendAsync(HttpMethod.Post, requestUri, payload, authorization, cancellationToken); /// /// Puts the specified URL. /// /// The type of response object. /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested type. /// public static async Task Put( Uri requestUri, object payload, string? authorization = null, CancellationToken ct = default) { var jsonString = await PutString(requestUri, payload, authorization, ct) .ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Puts the specified URL. /// /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested collection of key/value pairs. /// public static async Task?> Put( Uri requestUri, object payload, string? authorization = null, CancellationToken cancellationToken = default) { var response = await Put(requestUri, payload, authorization, cancellationToken) .ConfigureAwait(false); return response as IDictionary; } /// /// Puts as string. /// /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// url. /// Error PUT JSON. public static Task PutString( Uri requestUri, object payload, string? authorization = null, CancellationToken ct = default) => SendAsync(HttpMethod.Put, requestUri, payload, authorization, ct); /// /// Gets as string. /// /// The request URI. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// url. /// Error GET JSON. public static Task GetString( Uri requestUri, string? authorization = null, CancellationToken ct = default) => GetString(requestUri, null, authorization, ct); /// /// Gets the string. /// /// The URI. /// The headers. /// The authorization. /// The ct. /// /// A task with a result of the requested string. /// public static async Task GetString( Uri uri, IDictionary>? headers, string? authorization = null, CancellationToken ct = default) { var response = await GetHttpContent(uri, ct, authorization, headers) .ConfigureAwait(false); return await response.ReadAsStringAsync() .ConfigureAwait(false); } /// /// Gets the specified URL and return the JSON data as object /// with optional authorization token. /// /// The response type. /// The request URI. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested type. /// public static async Task Get( Uri requestUri, string? authorization = null, CancellationToken ct = default) { var jsonString = await GetString(requestUri, authorization, ct) .ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Gets the specified URL and return the JSON data as object /// with optional authorization token. /// /// The response type. /// The request URI. /// The headers. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested type. /// public static async Task Get( Uri requestUri, IDictionary>? headers, string? authorization = null, CancellationToken ct = default) { var jsonString = await GetString(requestUri, headers, authorization, ct) .ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Gets the binary. /// /// The request URI. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested byte array. /// /// url. /// Error GET Binary. public static async Task GetBinary( Uri requestUri, string? authorization = null, CancellationToken ct = default) { var response = await GetHttpContent(requestUri, ct, authorization) .ConfigureAwait(false); return await response.ReadAsByteArrayAsync() .ConfigureAwait(false); } /// /// Authenticate against a web server using Bearer Token. /// /// The request URI. /// The username. /// The password. /// The cancellation token. /// /// A task with a Dictionary with authentication data. /// /// url /// or /// username. /// Error Authenticating. public static async Task?> Authenticate( Uri requestUri, string username, string password, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullException(nameof(username)); // ignore empty password for now var content = $"grant_type=password&username={username}&password={password}"; using var requestContent = new StringContent(content, Encoding.UTF8, FormType); var response = await HttpClient.PostAsync(requestUri, requestContent, ct).ConfigureAwait(false); if (!response.IsSuccessStatusCode) throw new SecurityException($"Error Authenticating. Status code: {response.StatusCode}."); var jsonPayload = await response.Content.ReadAsStringAsync().ConfigureAwait(false); return Json.Deserialize(jsonPayload) as IDictionary; } /// /// Posts the file. /// /// The request URI. /// The buffer. /// Name of the file. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// public static Task PostFileString( Uri requestUri, byte[] buffer, string fileName, string? authorization = null, CancellationToken ct = default) => PostString(requestUri, new { Filename = fileName, Data = buffer }, authorization, ct); /// /// Posts the file. /// /// The response type. /// The request URI. /// The buffer. /// Name of the file. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// public static Task PostFile( Uri requestUri, byte[] buffer, string fileName, string? authorization = null, CancellationToken ct = default) => Post(requestUri, new { Filename = fileName, Data = buffer }, authorization, ct); /// /// Sends the asynchronous request. /// /// The method. /// The request URI. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// requestUri. /// Error {method} JSON. public static async Task SendAsync( HttpMethod method, Uri requestUri, object payload, string? authorization = null, CancellationToken ct = default) { using var response = await GetResponse(requestUri, authorization, null, payload, method, ct).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { throw new JsonRequestException( $"Error {method} JSON", (int)response.StatusCode, await response.Content.ReadAsStringAsync().ConfigureAwait(false)); } return await response.Content.ReadAsStringAsync() .ConfigureAwait(false); } private static async Task GetHttpContent( Uri uri, CancellationToken ct, string? authorization = null, IDictionary>? headers = null) { var response = await GetResponse(uri, authorization, headers, ct: ct) .ConfigureAwait(false); return response.IsSuccessStatusCode ? response.Content : throw new JsonRequestException("Error GET", (int)response.StatusCode); } private static async Task GetResponse( Uri uri, string? authorization, IDictionary>? headers, object? payload = null, HttpMethod? method = default, CancellationToken ct = default) { if (uri == null) throw new ArgumentNullException(nameof(uri)); using var requestMessage = new HttpRequestMessage(method ?? HttpMethod.Get, uri); if (!string.IsNullOrWhiteSpace(authorization)) { requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Bearer", authorization); } if (headers != null) { foreach (var header in headers) requestMessage.Headers.Add(header.Key, header.Value); } if (payload != null && requestMessage.Method != HttpMethod.Get) { requestMessage.Content = new StringContent(Json.Serialize(payload), Encoding.UTF8, JsonMimeType); } return await HttpClient.SendAsync(requestMessage, ct) .ConfigureAwait(false); } } }