RaspberryIO/Unosquare.Swan/Networking/JsonClient.cs

378 lines
15 KiB
C#
Raw Permalink Normal View History

2019-12-03 18:44:25 +01:00
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 {
/// <summary>
/// Represents a HttpClient with extended methods to use with JSON payloads
/// and bearer tokens authentication.
/// </summary>
public static class JsonClient {
private const String JsonMimeType = "application/json";
2019-02-17 14:08:57 +01:00
/// <summary>
2019-12-03 18:44:25 +01:00
/// Post a object as JSON with optional authorization token.
2019-02-17 14:08:57 +01:00
/// </summary>
2019-12-03 18:44:25 +01:00
/// <typeparam name="T">The type of response object.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested type.</returns>
public static async Task<T> Post<T>(
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<T>(jsonString) : default;
}
/// <summary>
/// Posts a object as JSON with optional authorization token and retrieve an object
/// or an error.
/// </summary>
/// <typeparam name="T">The type of response object.</typeparam>
/// <typeparam name="TE">The type of the error.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="httpStatusError">The HTTP status error.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested type or an error object.</returns>
public static async Task<OkOrError<T, TE>> PostOrError<T, TE>(
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<T, TE>.FromOk(!String.IsNullOrEmpty(jsonString)
2019-02-17 14:08:57 +01:00
? Json.Deserialize<T>(jsonString)
2019-12-03 18:44:25 +01:00
: default)
: (Int32)response.StatusCode == httpStatusError
? OkOrError<T, TE>.FromError(!String.IsNullOrEmpty(jsonString)
2019-02-17 14:08:57 +01:00
? Json.Deserialize<TE>(jsonString)
2019-12-03 18:44:25 +01:00
: default)
: new OkOrError<T, TE>();
}
}
/// <summary>
/// Posts the specified URL.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result as a collection of key/value pairs.</returns>
public static async Task<IDictionary<String, Object>> 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<String, Object>;
}
/// <summary>
/// Posts the specified URL.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>
/// A task with a result of the requested string.
/// </returns>
/// <exception cref="ArgumentNullException">url.</exception>
/// <exception cref="JsonRequestException">Error POST JSON.</exception>
public static Task<String> PostString(
String url,
Object payload,
String authorization = null,
CancellationToken ct = default) => SendAsync(HttpMethod.Post, url, payload, authorization, ct);
/// <summary>
/// Puts the specified URL.
/// </summary>
/// <typeparam name="T">The type of response object.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested type.</returns>
public static async Task<T> Put<T>(
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<T>(jsonString) : default;
}
/// <summary>
/// Puts the specified URL.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested collection of key/value pairs.</returns>
public static async Task<IDictionary<String, Object>> Put(
String url,
Object payload,
String authorization = null,
CancellationToken ct = default) {
Object response = await Put<Object>(url, payload, authorization, ct).ConfigureAwait(false);
return response as IDictionary<String, Object>;
}
/// <summary>
/// Puts as string.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>
/// A task with a result of the requested string.
/// </returns>
/// <exception cref="ArgumentNullException">url.</exception>
/// <exception cref="JsonRequestException">Error PUT JSON.</exception>
public static Task<String> PutString(
String url,
Object payload,
String authorization = null,
CancellationToken ct = default) => SendAsync(HttpMethod.Put, url, payload, authorization, ct);
/// <summary>
/// Gets as string.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>
/// A task with a result of the requested string.
/// </returns>
/// <exception cref="ArgumentNullException">url.</exception>
/// <exception cref="JsonRequestException">Error GET JSON.</exception>
public static async Task<String> GetString(
String url,
String authorization = null,
CancellationToken ct = default) {
HttpContent response = await GetHttpContent(url, authorization, ct).ConfigureAwait(false);
return await response.ReadAsStringAsync().ConfigureAwait(false);
}
/// <summary>
/// Gets the specified URL and return the JSON data as object
/// with optional authorization token.
/// </summary>
/// <typeparam name="T">The response type.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested type.</returns>
public static async Task<T> Get<T>(
String url,
String authorization = null,
CancellationToken ct = default) {
String jsonString = await GetString(url, authorization, ct).ConfigureAwait(false);
return !String.IsNullOrEmpty(jsonString) ? Json.Deserialize<T>(jsonString) : default;
}
/// <summary>
/// Gets the binary.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>
/// A task with a result of the requested byte array.
/// </returns>
/// <exception cref="ArgumentNullException">url.</exception>
/// <exception cref="JsonRequestException">Error GET Binary.</exception>
public static async Task<Byte[]> GetBinary(
String url,
String authorization = null,
CancellationToken ct = default) {
HttpContent response = await GetHttpContent(url, authorization, ct).ConfigureAwait(false);
return await response.ReadAsByteArrayAsync().ConfigureAwait(false);
}
/// <summary>
/// Authenticate against a web server using Bearer Token.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="username">The username.</param>
/// <param name="password">The password.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>
/// A task with a Dictionary with authentication data.
/// </returns>
/// <exception cref="ArgumentNullException">
/// url
/// or
/// username.
/// </exception>
/// <exception cref="SecurityException">Error Authenticating.</exception>
public static async Task<IDictionary<String, Object>> 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(
2019-02-17 14:08:57 +01:00
$"grant_type=password&username={username}&password={password}",
Encoding.UTF8,
2019-12-03 18:44:25 +01:00
"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<String, Object>;
}
}
/// <summary>
/// Posts the file.
/// </summary>
/// <param name="url">The URL.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="fileName">Name of the file.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>
/// A task with a result of the requested string.
/// </returns>
public static Task<String> PostFileString(
String url,
Byte[] buffer,
String fileName,
String authorization = null,
CancellationToken ct = default) => PostString(url, new {
Filename = fileName, Data = buffer
}, authorization, ct);
/// <summary>
/// Posts the file.
/// </summary>
/// <typeparam name="T">The response type.</typeparam>
/// <param name="url">The URL.</param>
/// <param name="buffer">The buffer.</param>
/// <param name="fileName">Name of the file.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested string.</returns>
public static Task<T> PostFile<T>(
String url,
Byte[] buffer,
String fileName,
String authorization = null,
CancellationToken ct = default) => Post<T>(url, new {
Filename = fileName, Data = buffer
}, authorization, ct);
/// <summary>
/// Sends the asynchronous request.
/// </summary>
/// <param name="method">The method.</param>
/// <param name="url">The URL.</param>
/// <param name="payload">The payload.</param>
/// <param name="authorization">The authorization.</param>
/// <param name="ct">The cancellation token.</param>
/// <returns>A task with a result of the requested string.</returns>
public static async Task<String> 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<HttpContent> 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;
}
}
}
2019-02-17 14:08:57 +01:00
}