namespace Unosquare.Swan.Networking { using System; using Exceptions; using Models; using Formatters; using System.Collections.Generic; using System.Net.Http; 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"; /// /// Post a object as JSON with optional authorization token. /// /// The type of response object. /// The URL. /// The payload. /// The authorization. /// The cancellation token. /// A task with a result of the requested type. public static async Task Post( string url, object payload, string authorization = null, CancellationToken ct = default) { var jsonString = await PostString(url, payload, authorization, ct).ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Posts a object as JSON with optional authorization token and retrieve an object /// or an error. /// /// The type of response object. /// The type of the error. /// The URL. /// The payload. /// The HTTP status error. /// The authorization. /// The cancellation token. /// A task with a result of the requested type or an error object. public static async Task> PostOrError( string url, object payload, int httpStatusError = 500, string authorization = null, CancellationToken ct = default) { using (var httpClient = GetHttpClientWithAuthorizationHeader(authorization)) { var payloadJson = new StringContent(Json.Serialize(payload), Encoding.UTF8, JsonMimeType); var response = await httpClient.PostAsync(url, payloadJson, ct).ConfigureAwait(false); var jsonString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); if (response.StatusCode == System.Net.HttpStatusCode.OK) { return OkOrError.FromOk(!string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default); } if ((int) response.StatusCode == httpStatusError) { return OkOrError.FromError(!string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default); } return new OkOrError(); } } /// /// Posts the specified URL. /// /// The URL. /// The payload. /// The authorization. /// The cancellation token. /// A task with a result as a collection of key/value pairs. public static async Task> Post( string url, object payload, string authorization = null, CancellationToken ct = default) { var jsonString = await PostString(url, payload, authorization, ct).ConfigureAwait(false); return string.IsNullOrWhiteSpace(jsonString) ? default : Json.Deserialize(jsonString) as IDictionary; } /// /// Posts the specified URL. /// /// The URL. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// url. /// Error POST JSON. public static Task PostString( string url, object payload, string authorization = null, CancellationToken ct = default) => SendAsync(HttpMethod.Post, url, payload, authorization, ct); /// /// Puts the specified URL. /// /// The type of response object. /// The URL. /// The payload. /// The authorization. /// The cancellation token. /// A task with a result of the requested type. public static async Task Put( string url, object payload, string authorization = null, CancellationToken ct = default) { var jsonString = await PutString(url, payload, authorization, ct).ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Puts the specified URL. /// /// The URL. /// 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( string url, object payload, string authorization = null, CancellationToken ct = default) { var response = await Put(url, payload, authorization, ct).ConfigureAwait(false); return response as IDictionary; } /// /// Puts as string. /// /// The URL. /// The payload. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// url. /// Error PUT JSON. public static Task PutString( string url, object payload, string authorization = null, CancellationToken ct = default) => SendAsync(HttpMethod.Put, url, payload, authorization, ct); /// /// Gets as string. /// /// The URL. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// /// url. /// Error GET JSON. public static async Task GetString( string url, string authorization = null, CancellationToken ct = default) { var response = await GetHttpContent(url, authorization, ct).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 URL. /// The authorization. /// The cancellation token. /// A task with a result of the requested type. public static async Task Get( string url, string authorization = null, CancellationToken ct = default) { var jsonString = await GetString(url, authorization, ct).ConfigureAwait(false); return !string.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default; } /// /// Gets the binary. /// /// The URL. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested byte array. /// /// url. /// Error GET Binary. public static async Task GetBinary( string url, string authorization = null, CancellationToken ct = default) { var response = await GetHttpContent(url, authorization, ct).ConfigureAwait(false); return await response.ReadAsByteArrayAsync().ConfigureAwait(false); } /// /// Authenticate against a web server using Bearer Token. /// /// The URL. /// 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( string url, string username, string password, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullException(nameof(username)); using (var httpClient = new HttpClient()) { // ignore empty password for now var requestContent = new StringContent( $"grant_type=password&username={username}&password={password}", Encoding.UTF8, "application/x-www-form-urlencoded"); var response = await httpClient.PostAsync(url, requestContent, ct).ConfigureAwait(false); if (response.IsSuccessStatusCode == false) 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 URL. /// The buffer. /// Name of the file. /// The authorization. /// The cancellation token. /// /// A task with a result of the requested string. /// public static Task PostFileString( string url, byte[] buffer, string fileName, string authorization = null, CancellationToken ct = default) { return PostString(url, new {Filename = fileName, Data = buffer}, authorization, ct); } /// /// Posts the file. /// /// The response type. /// The URL. /// The buffer. /// Name of the file. /// The authorization. /// The cancellation token. /// A task with a result of the requested string. public static Task PostFile( string url, byte[] buffer, string fileName, string authorization = null, CancellationToken ct = default) { return Post(url, new {Filename = fileName, Data = buffer}, authorization, ct); } /// /// Sends the asynchronous request. /// /// The method. /// The URL. /// The payload. /// The authorization. /// The cancellation token. /// A task with a result of the requested string. public static async Task SendAsync(HttpMethod method, string url, object payload, string authorization = null, CancellationToken ct = default) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); using (var httpClient = GetHttpClientWithAuthorizationHeader(authorization)) { var payloadJson = new StringContent(Json.Serialize(payload), Encoding.UTF8, JsonMimeType); var response = await httpClient .SendAsync(new HttpRequestMessage(method, url) {Content = payloadJson}, ct).ConfigureAwait(false); if (response.IsSuccessStatusCode == false) { throw new JsonRequestException( $"Error {method} JSON", (int) response.StatusCode, await response.Content.ReadAsStringAsync().ConfigureAwait(false)); } return await response.Content.ReadAsStringAsync().ConfigureAwait(false); } } private static HttpClient GetHttpClientWithAuthorizationHeader(string authorization) { var httpClient = new HttpClient(); if (string.IsNullOrWhiteSpace(authorization) == false) { httpClient.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", authorization); } return httpClient; } private static async Task GetHttpContent( string url, string authorization, CancellationToken ct) { if (string.IsNullOrWhiteSpace(url)) throw new ArgumentNullException(nameof(url)); using (var httpClient = GetHttpClientWithAuthorizationHeader(authorization)) { var response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); if (response.IsSuccessStatusCode == false) throw new JsonRequestException("Error GET", (int) response.StatusCode); return response.Content; } } } }