Updated shared components used for proxy communication by the gateway

This commit is contained in:
Philip Dimitratos 2017-10-27 13:37:59 -07:00
Родитель 2a8dfd5bf2
Коммит 35dd9473fe
7 изменённых файлов: 168 добавлений и 15 удалений

Просмотреть файл

@ -0,0 +1,35 @@
using Sia.Shared.Exceptions;
using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Text;
namespace Sia.Shared.Authentication.Http
{
public class HttpClientLookup
{
private IDictionary<string, string> _endpointToBaseUrl;
private IDictionary<string, HttpClient> _endpointToHttpClient { get; }
public HttpClientLookup()
{
_endpointToBaseUrl = new Dictionary<string, string>();
_endpointToHttpClient = new Dictionary<string, HttpClient>();
}
public void RegisterEndpoint(string endpointName, string baseUrl)
=> _endpointToBaseUrl.Add(endpointName, baseUrl);
public HttpClient GetClientForEndpoint(string endpointName)
{
if (_endpointToHttpClient.TryGetValue(endpointName, out HttpClient storedHttpClient)) return storedHttpClient;
if (!_endpointToBaseUrl.TryGetValue(endpointName, out string baseUrl)) throw new EndpointNotConfiguredException(endpointName);
var client = HttpClientExtensions.CreateHttpClient(baseUrl);
_endpointToHttpClient.Add(endpointName, client);
return client;
}
}
}

Просмотреть файл

@ -5,7 +5,7 @@ namespace Sia.Shared.Exceptions
{
public static class ConvertStatusToException
{
public static void ThrowExceptionOnUnsuccessfulStatus<T>(this IResponse<T> response)
public static void ThrowExceptionOnUnsuccessfulStatus(this IResponse response)
{
if (response.IsSuccessStatusCode)
{

Просмотреть файл

@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Sia.Shared.Exceptions
{
public class EndpointNotConfiguredException : Exception
{
public EndpointNotConfiguredException(string endpointName)
: base($"No base URI was configured for ${endpointName}")
{
}
}
}

Просмотреть файл

@ -3,19 +3,23 @@ using Sia.Shared.Authentication;
namespace Sia.Shared.Requests
{
public abstract class AuthenticatedRequest<T> : IRequest<T>
public abstract class AuthenticatedRequest<T> : AuthenticatedRequestBase, IRequest<T>
{
protected AuthenticatedRequest(AuthenticatedUserContext userContext)
protected AuthenticatedRequest(AuthenticatedUserContext userContext) : base(userContext)
{
UserContext = userContext;
}
public AuthenticatedUserContext UserContext { get; private set; }
}
public abstract class AuthenticatedRequest : IRequest
public abstract class AuthenticatedRequest : AuthenticatedRequestBase, IRequest
{
protected AuthenticatedRequest(AuthenticatedUserContext userContext)
protected AuthenticatedRequest(AuthenticatedUserContext userContext) : base(userContext)
{
}
}
public abstract class AuthenticatedRequestBase
{
protected AuthenticatedRequestBase(AuthenticatedUserContext userContext)
{
UserContext = userContext;
}

Просмотреть файл

@ -0,0 +1,83 @@
using MediatR;
using Sia.Shared.Authentication.Http;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using System.Net.Http;
using Sia.Shared.Protocol;
using Sia.Shared.Exceptions;
using Newtonsoft.Json;
using Sia.Shared.Requests;
using System.Net.Http.Headers;
namespace Sia.Shared.Extensions.Mediatr
{
public abstract class ProxyHandler<TRequest, TResult> : ProxyHandlerBase<TRequest>, IAsyncRequestHandler<TRequest, TResult>
where TRequest : AuthenticatedRequest<TResult>
{
protected ProxyHandler(HttpClientLookup clientFactory, string endpointName)
: base(clientFactory, endpointName)
{
}
public virtual async Task<TResult> Handle(TRequest request)
{
var httpResponse = await SendRequest(request);
var logicalResponse = await Response<TResult>.Create(httpResponse);
logicalResponse.ThrowExceptionOnUnsuccessfulStatus();
return logicalResponse.Value;
}
}
public abstract class ProxyHandler<TRequest> : ProxyHandlerBase<TRequest>, IAsyncRequestHandler<TRequest>
where TRequest : AuthenticatedRequest
{
protected ProxyHandler(HttpClientLookup clientFactory, string endpointName)
: base(clientFactory, endpointName)
{
}
public virtual async Task Handle(TRequest request)
{
var httpResponse = await SendRequest(request);
var logicalResponse = await Response.Create(httpResponse);
logicalResponse.ThrowExceptionOnUnsuccessfulStatus();
}
}
public abstract class ProxyHandlerBase<TRequest>
where TRequest: AuthenticatedRequestBase
{
protected readonly HttpClient _client;
protected abstract HttpMethod Method();
protected abstract string RelativeUri(TRequest request);
protected abstract object MessageContent(TRequest request);
protected virtual async Task<HttpResponseMessage> SendRequest(TRequest request)
{
var message = new HttpRequestMessage(Method(), RelativeUri(request));
AddContentToMessage(request, message);
await AddAuthorizationToMessage(request, message);
return await _client.SendAsync(message);
}
protected virtual void AddContentToMessage(TRequest request, HttpRequestMessage message)
{
var content = MessageContent(request);
if (content is null) return;
var encodedContent = JsonConvert.SerializeObject(content);
var httpContent = new StringContent(encodedContent, Encoding.UTF8, HttpClientExtensions.JsonMediaType);
message.Content = httpContent;
}
protected virtual async Task AddAuthorizationToMessage(TRequest request, HttpRequestMessage message)
{
var tokenResult = await HttpClientExtensions.AcquireTokenAsync(request.UserContext);
message.Headers.Authorization = new AuthenticationHeaderValue(request.UserContext.AuthConfig.Scheme, tokenResult);
}
protected ProxyHandlerBase(HttpClientLookup clientFactory, string endpointName)
{
_client = clientFactory.GetClientForEndpoint(endpointName);
}
}
}

Просмотреть файл

@ -2,11 +2,16 @@
namespace Sia.Shared.Protocol
{
public interface IResponse<T>
public interface IResponse<T> : IResponse
{
T Value { get; }
}
public interface IResponse
{
HttpStatusCode StatusCode { get; }
bool IsSuccessStatusCode { get; }
string Content { get; }
T Value { get; }
}
}

Просмотреть файл

@ -5,13 +5,11 @@ using System.Threading.Tasks;
namespace Sia.Shared.Protocol
{
public class Response<T> : IResponse<T>
public class Response<T> : Response, IResponse<T>
{
public HttpStatusCode StatusCode { get; private set; }
public bool IsSuccessStatusCode { get; private set; }
public string Content { get; private set; }
public T Value { get; private set; }
public static async Task<Response<T>> Create(HttpResponseMessage message)
public static new async Task<Response<T>> Create(HttpResponseMessage message)
{
var response = new Response<T>();
response.IsSuccessStatusCode = message.IsSuccessStatusCode;
@ -28,4 +26,18 @@ namespace Sia.Shared.Protocol
return response;
}
}
public class Response : IResponse
{
public HttpStatusCode StatusCode { get; protected set; }
public bool IsSuccessStatusCode { get; protected set; }
public string Content { get; protected set; }
public static async Task<Response> Create(HttpResponseMessage message)
=> new Response()
{
IsSuccessStatusCode = message.IsSuccessStatusCode,
StatusCode = message.StatusCode,
Content = await message.Content.ReadAsStringAsync()
};
}
}