using RestEase.Implementation;
using System;
using System.Linq;
using System.Net.Http;
using System.Reflection;
using System.Collections.Generic;
namespace RestEase
{
///
/// Creates REST API clients from a suitable interface. Your single point of interaction with RestEase
///
public class RestClient
{
private static readonly MethodInfo forInstanceGenericMethodInfo = typeof(RestClient).GetTypeInfo().GetMethods().First(x => x.Name == "For" && !x.IsStatic && x.GetParameters().Length == 0 && x.IsGenericMethod);
private static readonly MethodInfo forStaticGenericMethodInfo = typeof(RestClient).GetTypeInfo().GetMethods().First(x => x.Name == "For" && x.IsStatic && x.GetParameters().Length == 1 && x.GetParameters()[0].ParameterType == typeof(IRequester) && x.IsGenericMethod);
private static readonly ImplementationFactory factory = new ImplementationFactory();
///
/// Name of the assembly in which interface implementations are built. Use in [assembly: InternalsVisibleTo(RestEase.FactoryAssemblyName)] to allow clients to be generated for internal interface types
///
public const string FactoryAssemblyName = "RestEaseFactory";
///
/// Name of the key in that a request's is stored
///
public const string HttpRequestMessageRequestInfoPropertyKey = "RestEaseRequestInfo";
private readonly HttpClient httpClient;
#pragma warning disable CS0618 // Type or member is obsolete
///
/// Gets or sets the deserializer used to deserialize responses
///
///
/// Defaults to .
///
/// This has the type for backwards-compatibility reasons. You should assign
/// an instance of
///
public IResponseDeserializer? ResponseDeserializer { get; set; }
///
/// Gets or sets the serializer used to serialize request bodies (when [Body(BodySerializationMethod.Serialized)] is used)
///
///
/// Defaults to .
///
/// This has the type for backwards-compatibility reasons. You should assign
/// an instance of .
///
public IRequestBodySerializer? RequestBodySerializer { get; set; }
///
/// Gets or sets the serializer used to serialize path parameters (when [Path(PathSerializationMethod.Serialized)] is used)
///
///
/// Has no default value, explicit serializer implementation must be provided.
///
public RequestPathParamSerializer? RequestPathParamSerializer { get; set; }
///
/// Gets or sets the serializer used to serialize query parameters (when [Query(QuerySerializationMethod.Serialized)] is used)
///
///
/// Defaults to .
///
/// This has the type for backwards-compatibility reasons. You should assign
/// an instance of .
///
public IRequestQueryParamSerializer? RequestQueryParamSerializer { get; set; }
#pragma warning restore CS0618 // Type or member is obsolete
///
/// Gets or sets the builder to use to construct encoded query strings from various parmaeters
///
///
/// Defaults to null, in which case the default building logic is used
///
public QueryStringBuilder? QueryStringBuilder { get; set; }
///
/// Gets or sets the used to format items using
///
///
/// Defaults to null, in which case the current culture is used.
///
public IFormatProvider? FormatProvider { get; set; }
///
/// Initialises a new instance of the class, with the given Base URL
///
/// Base URL of the API
public RestClient(string baseUrl)
{
if (baseUrl == null)
throw new ArgumentNullException(nameof(baseUrl));
this.httpClient = this.Initialize(new HttpClientHandler(), new Uri(baseUrl));
}
///
/// Initialises a new instance of the class, with the given Base URL
///
/// Base URL of the API
public RestClient(Uri baseUrl)
{
if (baseUrl == null)
throw new ArgumentNullException(nameof(baseUrl));
this.httpClient = this.Initialize(new HttpClientHandler(), baseUrl);
}
///
/// Initialises a new instance of the class, with the given Base URL and request modifier
///
/// Base URL of the API
/// Delegate called on every request
public RestClient(string baseUrl, RequestModifier requestModifier)
{
if (baseUrl == null)
throw new ArgumentNullException(nameof(baseUrl));
if (requestModifier == null)
throw new ArgumentNullException(nameof(requestModifier));
this.httpClient = this.Initialize(new ModifyingClientHttpHandler(requestModifier), new Uri(baseUrl));
}
///
/// Initialises a new instance of the class, with the given Base URL and request modifier
///
/// Base URL of the API
/// Delegate called on every request
public RestClient(Uri baseUrl, RequestModifier requestModifier)
{
if (baseUrl == null)
throw new ArgumentNullException(nameof(baseUrl));
if (requestModifier == null)
throw new ArgumentNullException(nameof(requestModifier));
this.httpClient = this.Initialize(new ModifyingClientHttpHandler(requestModifier), baseUrl);
}
private HttpClient Initialize(HttpMessageHandler messageHandler, Uri baseUrl)
{
return new HttpClient(messageHandler)
{
BaseAddress = baseUrl,
};
}
///
/// Initialises a new instance of the class, using the given HttpClient
///
/// HttpClient to use
public RestClient(HttpClient httpClient)
{
this.httpClient = httpClient ?? throw new ArgumentNullException(nameof(httpClient));
}
///
/// Create an implementation for the given API interface
///
/// Type of interface to implement
/// An implementation which can be used to make REST requests
public object For(Type type)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var method = forInstanceGenericMethodInfo.MakeGenericMethod(type);
return method.Invoke(this, new object[0]);
}
///
/// Create an implementation for the given API interface
///
/// Type of interface to implement
/// An implementation which can be used to make REST requests
public T For()
{
var requester = this.CreateRequester();
return factory.CreateImplementation(requester);
}
private Requester CreateRequester()
{
var requester = new Requester(this.httpClient);
if (this.RequestBodySerializer is RequestBodySerializer requestBodySerializer)
requester.RequestBodySerializer = requestBodySerializer;
else if (this.RequestBodySerializer != null)
requester.RequestBodySerializer = new RequestBodySerializerWrapper(this.RequestBodySerializer);
else
requester.RequestBodySerializer = new JsonRequestBodySerializer();
requester.RequestPathParamSerializer = this.RequestPathParamSerializer;
if (this.RequestQueryParamSerializer is RequestQueryParamSerializer requestQueryParamSerializer)
requester.RequestQueryParamSerializer = requestQueryParamSerializer;
else if (this.RequestQueryParamSerializer != null)
requester.RequestQueryParamSerializer = new RequestQueryParamSerializerWrapper(this.RequestQueryParamSerializer);
else
requester.RequestQueryParamSerializer = new JsonRequestQueryParamSerializer();
if (this.ResponseDeserializer is ResponseDeserializer responseDeserializer)
requester.ResponseDeserializer = responseDeserializer;
else if (this.ResponseDeserializer != null)
requester.ResponseDeserializer = new ResponseDeserializerWrapper(this.ResponseDeserializer);
else
requester.ResponseDeserializer = new JsonResponseDeserializer();
requester.QueryStringBuilder = this.QueryStringBuilder;
requester.FormatProvider = this.FormatProvider;
return requester;
}
///
/// Create a client using the given IRequester. This gives you the greatest ability to customise functionality
///
/// Interface representing the API
/// IRequester to use
/// An implementation of that interface which you can use to invoke the API
public static object For(Type type, IRequester requester)
{
if (type == null)
throw new ArgumentNullException(nameof(type));
var method = forStaticGenericMethodInfo.MakeGenericMethod(type);
return method.Invoke(null, new object[] { requester });
}
///
/// Create a client using the given IRequester. This gives you the greatest ability to customise functionality
///
/// Interface representing the API
/// IRequester to use
/// An implementation of that interface which you can use to invoke the API
public static T For(IRequester requester)
{
return factory.CreateImplementation(requester);
}
///
/// Shortcut to create a client using the given url
///
/// Interface representing the API
/// Base URL
/// An implementation of that interface which you can use to invoke the API
public static T For(string baseUrl)
{
return new RestClient(baseUrl).For();
}
///
/// Shortcut to create a client using the given url
///
/// Interface representing the API
/// Base URL
/// An implementation of that interface which you can use to invoke the API
public static T For(Uri baseUrl)
{
return new RestClient(baseUrl).For();
}
///
/// Shortcut to create a client using the given HttpClient
///
/// Interface representing the API
/// HttpClient to use to make requests
/// An implementation of that interface which you can use to invoke the API
public static T For(HttpClient httpClient)
{
return new RestClient(httpClient).For();
}
///
/// Shortcut to create a client using the given URL and request interceptor
///
/// Interface representing the API
/// Base URL
/// Delegate called on every request
/// An implementation of that interface which you can use to invoke the API
public static T For(string baseUrl, RequestModifier requestModifier)
{
return new RestClient(baseUrl, requestModifier).For();
}
///
/// Shortcut to create a client using the given URL and request interceptor
///
/// Interface representing the API
/// Base URL
/// Delegate called on every request
/// An implementation of that interface which you can use to invoke the API
public static T For(Uri baseUrl, RequestModifier requestModifier)
{
return new RestClient(baseUrl, requestModifier).For();
}
///
/// Create a client using the given base URL, and custom serializer and/or deserializer
///
/// Interface representing the API
/// Base URL
/// Deserializer to use when deserializing responses
/// Serializer to use when serializing request bodies, when appropriate
/// An implementation of that interface which you can use to invoke the API
[Obsolete("Use 'new RestClient(baseUrl) { ResponseDeserializer = responseDeserializer, RequestBodySerializer = requestBodySerializer }.For()' instead")]
public static T For(string baseUrl, ResponseDeserializer? responseDeserializer = null, RequestBodySerializer? requestBodySerializer = null)
{
return new RestClient(baseUrl) { ResponseDeserializer = responseDeserializer, RequestBodySerializer = requestBodySerializer }.For();
}
///
/// Create a client using the given base URL, and custom serializer and/or deserializer
///
/// Interface representing the API
/// Base URL
/// Delegate called on every request
/// Deserializer to use when deserializing responses
/// Serializer to use when serializing request bodiess, when appropriate
/// An implementation of that interface which you can use to invoke the API
[Obsolete("Use 'new RestClient(baseUrl, requestModifier) { ResponseDeserializer = responseDeserializer, RequestBodySerializer = requestBodySerializer }.For()' instead")]
public static T For(string baseUrl, RequestModifier requestModifier, ResponseDeserializer? responseDeserializer = null, RequestBodySerializer? requestBodySerializer = null)
{
return new RestClient(baseUrl, requestModifier) { ResponseDeserializer = responseDeserializer, RequestBodySerializer = requestBodySerializer }.For();
}
///
/// Create a client using the given HttpClient, and custom serializer and/or deserializer
///
/// Interface representing the API
/// HttpClient to use to make requests
/// Deserializer to use when deserializing responses
/// Serializer to use when serializing request bodies, when appropriate
/// An implementation of that interface which you can use to invoke the API
[Obsolete("Use 'new RestClient(httpClient) { ResponseDeserializer = responseDeserializer, RequestBodySerializer = requestBodySerializer }.For()' instead")]
public static T For(HttpClient httpClient, ResponseDeserializer? responseDeserializer = null, RequestBodySerializer? requestBodySerializer = null)
{
return new RestClient(httpClient) { ResponseDeserializer = responseDeserializer, RequestBodySerializer = requestBodySerializer }.For();
}
#pragma warning disable CS0618 // Type or member is obsolete
private class RequestBodySerializerWrapper : RequestBodySerializer
{
private readonly IRequestBodySerializer serializer;
public RequestBodySerializerWrapper(IRequestBodySerializer serializer) =>
this.serializer = serializer;
public override HttpContent SerializeBody(T body, RequestBodySerializerInfo info) =>
this.serializer.SerializeBody(body);
}
private class RequestQueryParamSerializerWrapper : RequestQueryParamSerializer
{
private readonly IRequestQueryParamSerializer serializer;
public RequestQueryParamSerializerWrapper(IRequestQueryParamSerializer serializer) =>
this.serializer = serializer;
public override IEnumerable> SerializeQueryCollectionParam(string name, IEnumerable values, RequestQueryParamSerializerInfo info) =>
this.serializer.SerializeQueryCollectionParam(name, values, info);
public override IEnumerable> SerializeQueryParam(string name, T value, RequestQueryParamSerializerInfo info) =>
this.serializer.SerializeQueryParam(name, value, info);
}
private class ResponseDeserializerWrapper : ResponseDeserializer
{
private readonly IResponseDeserializer deserializer;
public ResponseDeserializerWrapper(IResponseDeserializer deserializer) =>
this.deserializer = deserializer;
public override T Deserialize(string? content, HttpResponseMessage response, ResponseDeserializerInfo info) =>
this.deserializer.Deserialize(content, response);
}
#pragma warning restore CS0618 // Type or member is obsolete
}
}