using System; using Unosquare.Swan.Exceptions; using Unosquare.Swan.Models; using Unosquare.Swan.Formatters; using System.Collections.Generic; using System.Net.Http; using System.Security; using System.Text; using System.Threading; using System.Threading.Tasks; namespace Unosquare.Swan.Networking { /// /// 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) { String 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, Int32 httpStatusError = 500, String authorization = null, CancellationToken ct = default) { using(HttpClient httpClient = GetHttpClientWithAuthorizationHeader(authorization)) { StringContent payloadJson = new StringContent(Json.Serialize(payload), Encoding.UTF8, JsonMimeType); HttpResponseMessage response = await httpClient.PostAsync(url, payloadJson, ct).ConfigureAwait(false); String jsonString = await response.Content.ReadAsStringAsync().ConfigureAwait(false); return response.StatusCode == System.Net.HttpStatusCode.OK ? OkOrError.FromOk(!String.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default) : (Int32)response.StatusCode == httpStatusError ? OkOrError.FromError(!String.IsNullOrEmpty(jsonString) ? Json.Deserialize(jsonString) : default) : 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) { String 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) { String 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) { Object 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) { HttpContent 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) { String 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) { HttpContent 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(HttpClient httpClient = new HttpClient()) { // ignore empty password for now StringContent requestContent = new StringContent( $"grant_type=password&username={username}&password={password}", Encoding.UTF8, "application/x-www-form-urlencoded"); HttpResponseMessage response = await httpClient.PostAsync(url, requestContent, ct).ConfigureAwait(false); if(response.IsSuccessStatusCode == false) { throw new SecurityException($"Error Authenticating. Status code: {response.StatusCode}."); } String 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) => 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) => 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(HttpClient httpClient = GetHttpClientWithAuthorizationHeader(authorization)) { StringContent payloadJson = new StringContent(Json.Serialize(payload), Encoding.UTF8, JsonMimeType); HttpResponseMessage response = await httpClient .SendAsync(new HttpRequestMessage(method, url) { Content = payloadJson }, ct).ConfigureAwait(false); if(response.IsSuccessStatusCode == false) { throw new JsonRequestException( $"Error {method} JSON", (Int32)response.StatusCode, await response.Content.ReadAsStringAsync().ConfigureAwait(false)); } return await response.Content.ReadAsStringAsync().ConfigureAwait(false); } } private static HttpClient GetHttpClientWithAuthorizationHeader(String authorization) { HttpClient 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(HttpClient httpClient = GetHttpClientWithAuthorizationHeader(authorization)) { HttpResponseMessage response = await httpClient.GetAsync(url, ct).ConfigureAwait(false); if(response.IsSuccessStatusCode == false) { throw new JsonRequestException("Error GET", (Int32)response.StatusCode); } return response.Content; } } } }