From 2c23aec026666ae64e1743b64cc742c310894506 Mon Sep 17 00:00:00 2001 From: Laurent Keusch <25180858+laurentksh@users.noreply.github.com> Date: Wed, 10 Nov 2021 14:57:46 +0100 Subject: [PATCH 1/3] -Added comments for ApiException. -Added comments for ApiResponse. -Added comments for most of the attributes. -Added comments for RestService. --- Refit/ApiException.cs | 67 +++++++++++++++++++++++++++++++++++++- Refit/ApiResponse.cs | 73 +++++++++++++++++++++++++++++++++++++++-- Refit/Attributes.cs | 60 ++++++++++++++++++++++++++++++++-- Refit/RestService.cs | 75 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 268 insertions(+), 7 deletions(-) diff --git a/Refit/ApiException.cs b/Refit/ApiException.cs index 0b442abaf..10adaa672 100644 --- a/Refit/ApiException.cs +++ b/Refit/ApiException.cs @@ -6,19 +6,60 @@ namespace Refit { - + /// + /// Represents an error that occured while sending an API request. + /// [Serializable] public class ApiException : Exception { + /// + /// HTTP response status code. + /// public HttpStatusCode StatusCode { get; } + + /// + /// The reason phrase which typically is sent by the server together with the status code. + /// public string? ReasonPhrase { get; } + + /// + /// HTTP response headers. + /// public HttpResponseHeaders Headers { get; } + + /// + /// The HTTP method used to send the request. + /// public HttpMethod HttpMethod { get; } + + /// + /// The used to send the HTTP request. + /// public Uri? Uri => RequestMessage.RequestUri; + + /// + /// The HTTP Request message used to send the request. + /// public HttpRequestMessage RequestMessage { get; } + + /// + /// HTTP response content headers as defined in RFC 2616. + /// public HttpContentHeaders? ContentHeaders { get; private set; } + + /// + /// HTTP Response content as string. + /// public string? Content { get; private set; } + + /// + /// Does the response have content? + /// public bool HasContent => !string.IsNullOrWhiteSpace(Content); + + /// + /// Refit settings used to send the request. + /// public RefitSettings RefitSettings { get; } protected ApiException(HttpRequestMessage message, HttpMethod httpMethod, string? content, HttpStatusCode statusCode, string? reasonPhrase, HttpResponseHeaders headers, RefitSettings refitSettings, Exception? innerException = null) : @@ -38,10 +79,24 @@ protected ApiException(string exceptionMessage, HttpRequestMessage message, Http Content = content; } + /// + /// Get the deserialized response content as nullable . + /// + /// Type to deserialize the content to + /// The response content deserialized as public async Task GetContentAsAsync() => HasContent ? await RefitSettings.ContentSerializer.FromHttpContentAsync(new StringContent(Content!)).ConfigureAwait(false) : default; + /// + /// Create an instance of . + /// + /// The HTTP Request message used to send the request. + /// The HTTP method used to send the request. + /// The HTTP Response message. + /// Refit settings used to sent the request. + /// Add an inner exception to the . + /// A newly created . #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods public static Task Create(HttpRequestMessage message, HttpMethod httpMethod, HttpResponseMessage response, RefitSettings refitSettings, Exception? innerException = null) #pragma warning restore VSTHRD200 // Use "Async" suffix for async methods @@ -50,6 +105,16 @@ public static Task Create(HttpRequestMessage message, HttpMethod h return Create(exceptionMessage, message, httpMethod, response, refitSettings, innerException); } + /// + /// Create an instance of with a custom exception message. + /// + /// A custom exception message. + /// The HTTP Request message used to send the request. + /// The HTTP method used to send the request. + /// The HTTP Response message. + /// Refit settings used to sent the request. + /// Add an inner exception to the . + /// A newly created . #pragma warning disable VSTHRD200 // Use "Async" suffix for async methods public static async Task Create(string exceptionMessage, HttpRequestMessage message, HttpMethod httpMethod, HttpResponseMessage response, RefitSettings refitSettings, Exception? innerException = null) #pragma warning restore VSTHRD200 // Use "Async" suffix for async methods diff --git a/Refit/ApiResponse.cs b/Refit/ApiResponse.cs index 77b95e5d2..5a5792564 100644 --- a/Refit/ApiResponse.cs +++ b/Refit/ApiResponse.cs @@ -14,11 +14,23 @@ internal static T Create(HttpResponseMessage resp, object? content, Re } } + /// + /// Implementation of that provides additional functionalities. + /// + /// public sealed class ApiResponse : IApiResponse { readonly HttpResponseMessage response; bool disposed; + /// + /// Create an instance of with type . + /// + /// Original HTTP Response message. + /// Response content. + /// Refit settings used to send the request. + /// The ApiException, if the request failed. + /// public ApiResponse(HttpResponseMessage response, T? content, RefitSettings settings, ApiException? error = null) { this.response = response ?? throw new ArgumentNullException(nameof(response)); @@ -27,16 +39,30 @@ public ApiResponse(HttpResponseMessage response, T? content, RefitSettings setti Settings = settings; } + /// + /// Deserialized request content as . + /// public T? Content { get; } - public RefitSettings Settings { get; } + /// + /// Refit settings used to send the request. + /// + public RefitSettings Settings { get; } + public HttpResponseHeaders Headers => response.Headers; + public HttpContentHeaders? ContentHeaders => response.Content?.Headers; + public bool IsSuccessStatusCode => response.IsSuccessStatusCode; + public string? ReasonPhrase => response.ReasonPhrase; + public HttpRequestMessage? RequestMessage => response.RequestMessage; + public HttpStatusCode StatusCode => response.StatusCode; + public Version Version => response.Version; + public ApiException? Error { get; private set; } @@ -45,6 +71,11 @@ public void Dispose() Dispose(true); } + /// + /// Ensures the request was successful by throwing an exception in case of failure + /// + /// The current + /// public async Task> EnsureSuccessStatusCodeAsync() { if (!IsSuccessStatusCode) @@ -70,20 +101,58 @@ void Dispose(bool disposing) } } + /// public interface IApiResponse : IApiResponse { + /// + /// Deserialized request content as . + /// T? Content { get; } } + /// + /// Base interface used to represent an API response. + /// public interface IApiResponse : IDisposable { + /// + /// HTTP response headers. + /// HttpResponseHeaders Headers { get; } + + /// + /// HTTP response content headers as defined in RFC 2616. + /// HttpContentHeaders? ContentHeaders { get; } + + /// + /// Indicates whether the request was successful. + /// bool IsSuccessStatusCode { get; } + + /// + /// HTTP response status code. + /// + HttpStatusCode StatusCode { get; } + + /// + /// The reason phrase which typically is sent by the server together with the status code. + /// string? ReasonPhrase { get; } + + /// + /// The HTTP Request message which led to this response. + /// HttpRequestMessage? RequestMessage { get; } - HttpStatusCode StatusCode { get; } + + /// + /// HTTP Message version. + /// Version Version { get; } + + /// + /// The object in case of unsuccessful response. + /// ApiException? Error { get; } } } diff --git a/Refit/Attributes.cs b/Refit/Attributes.cs index 3d03f227b..6236a5881 100644 --- a/Refit/Attributes.cs +++ b/Refit/Attributes.cs @@ -19,6 +19,9 @@ public virtual string Path } } + /// + /// Send the request with HTTP method 'GET'. + /// [AttributeUsage(AttributeTargets.Method)] public class GetAttribute : HttpMethodAttribute { @@ -30,6 +33,9 @@ public override HttpMethod Method } } + /// + /// Send the request with HTTP method 'POST'. + /// [AttributeUsage(AttributeTargets.Method)] public class PostAttribute : HttpMethodAttribute { @@ -41,6 +47,9 @@ public override HttpMethod Method } } + /// + /// Send the request with HTTP method 'PUT'. + /// [AttributeUsage(AttributeTargets.Method)] public class PutAttribute : HttpMethodAttribute { @@ -52,6 +61,9 @@ public override HttpMethod Method } } + /// + /// Send the request with HTTP method 'DELETE'. + /// [AttributeUsage(AttributeTargets.Method)] public class DeleteAttribute : HttpMethodAttribute { @@ -63,6 +75,9 @@ public override HttpMethod Method } } + /// + /// Send the request with HTTP method 'PATCH'. + /// [AttributeUsage(AttributeTargets.Method)] public class PatchAttribute : HttpMethodAttribute { @@ -74,6 +89,9 @@ public override HttpMethod Method } } + /// + /// Send the request with HTTP method 'OPTION'. + /// [AttributeUsage(AttributeTargets.Method)] public class OptionsAttribute : HttpMethodAttribute { @@ -85,6 +103,9 @@ public override HttpMethod Method } } + /// + /// Send the request with HTTP method 'HEAD'. + /// [AttributeUsage(AttributeTargets.Method)] public class HeadAttribute : HttpMethodAttribute { @@ -96,6 +117,12 @@ public override HttpMethod Method } } + /// + /// Send the request as multipart. + /// + /// + /// Currently, multipart methods only support the following parameter types: , array, , . + /// [AttributeUsage(AttributeTargets.Method)] public class MultipartAttribute : Attribute { @@ -132,6 +159,17 @@ public enum BodySerializationMethod Serialized } + /// + /// Set a parameter to be sent as the HTTP request's body. + /// + /// + /// There are four behaviors when sending a parameter as the request body:
+ /// - If the type is/implements , the content will be streamed via .
+ /// - If the type is , it will be used directly as the content unless [Body(BodySerializationMethod.Json)] is set + /// which will send it as a .
+ /// - If the parameter has the attribute [Body(BodySerializationMethod.UrlEncoded)], the content will be URL-encoded.
+ /// - For all other types, the object will be serialized using the content serializer specified in the request's . + ///
[AttributeUsage(AttributeTargets.Parameter)] public class BodyAttribute : Attribute { @@ -161,6 +199,9 @@ public BodyAttribute(BodySerializationMethod serializationMethod = BodySerializa public BodySerializationMethod SerializationMethod { get; protected set; } = BodySerializationMethod.Default; } + /// + /// Override the key that will be sent in the query string. + /// [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] public class AliasAsAttribute : Attribute { @@ -185,7 +226,7 @@ public AttachmentNameAttribute(string name) } /// - /// Allows you provide a Dictionary of headers to be added to the request. + /// Allows you to provide a Dictionary of headers to be added to the request. /// [AttributeUsage(AttributeTargets.Parameter)] public class HeaderCollectionAttribute : Attribute @@ -193,6 +234,9 @@ public class HeaderCollectionAttribute : Attribute } + /// + /// Add multiple headers to the request. + /// [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Method)] public class HeadersAttribute : Attribute { @@ -204,6 +248,9 @@ public HeadersAttribute(params string[] headers) public string[] Headers { get; } } + /// + /// Add a header to the request. + /// [AttributeUsage(AttributeTargets.Parameter)] public class HeaderAttribute : Attribute { @@ -236,6 +283,12 @@ public PropertyAttribute(string key) public string? Key { get; } } + /// + /// Add the Authorize header to the request with the value of the associated parameter. + /// + /// + /// Default authorization scheme: Bearer + /// [AttributeUsage(AttributeTargets.Parameter)] public class AuthorizeAttribute : Attribute { @@ -247,8 +300,10 @@ public AuthorizeAttribute(string scheme = "Bearer") public string Scheme { get; } } + /// + /// Associated value will be added to the request Uri as query-string, using a delimiter to split the values. (default: '.') + /// [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] // Property is to allow for form url encoded data - public class QueryAttribute : Attribute { CollectionFormat? collectionFormat; @@ -332,7 +387,6 @@ public CollectionFormat CollectionFormat } [AttributeUsage(AttributeTargets.Method)] - public class QueryUriFormatAttribute : Attribute { public QueryUriFormatAttribute(UriFormat uriFormat) diff --git a/Refit/RestService.cs b/Refit/RestService.cs index 77d85c4a2..4da9cd63c 100644 --- a/Refit/RestService.cs +++ b/Refit/RestService.cs @@ -8,11 +8,25 @@ public static class RestService { static readonly ConcurrentDictionary TypeMapping = new(); + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// The the implementation will use to send requests. + /// to use to build requests. + /// An instance that implements . public static T For(HttpClient client, IRequestBuilder builder) { - return (T)For(typeof(T), client, builder); + return (T)For(typeof(T), client, builder); } + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// The the implementation will use to send requests. + /// to use to configure the HttpClient. + /// An instance that implements . public static T For(HttpClient client, RefitSettings? settings) { var requestBuilder = RequestBuilder.ForType(settings); @@ -20,8 +34,21 @@ public static T For(HttpClient client, RefitSettings? settings) return For(client, requestBuilder); } + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// The the implementation will use to send requests. + /// An instance that implements . public static T For(HttpClient client) => For(client, (RefitSettings?)null); + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// Base address the implementation will use. + /// to use to configure the HttpClient. + /// An instance that implements . public static T For(string hostUrl, RefitSettings? settings) { var client = CreateHttpClient(hostUrl, settings); @@ -29,8 +56,21 @@ public static T For(string hostUrl, RefitSettings? settings) return For(client, settings); } + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// Base address the implementation will use. + /// An instance that implements . public static T For(string hostUrl) => For(hostUrl, null); + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// The the implementation will use to send requests. + /// to use to build requests. + /// An instance that implements . public static object For(Type refitInterfaceType, HttpClient client, IRequestBuilder builder) { var generatedType = TypeMapping.GetOrAdd(refitInterfaceType, GetGeneratedType(refitInterfaceType)); @@ -38,6 +78,13 @@ public static object For(Type refitInterfaceType, HttpClient client, IRequestBui return Activator.CreateInstance(generatedType, client, builder)!; } + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// The the implementation will use to send requests. + /// to use to configure the HttpClient. + /// An instance that implements . public static object For(Type refitInterfaceType, HttpClient client, RefitSettings? settings) { var requestBuilder = RequestBuilder.ForType(refitInterfaceType, settings); @@ -45,8 +92,21 @@ public static object For(Type refitInterfaceType, HttpClient client, RefitSettin return For(refitInterfaceType, client, requestBuilder); } + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// The the implementation will use to send requests. + /// An instance that implements . public static object For(Type refitInterfaceType, HttpClient client) => For(refitInterfaceType, client, (RefitSettings?)null); + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// Base address the implementation will use. + /// to use to configure the HttpClient. + /// An instance that implements . public static object For(Type refitInterfaceType, string hostUrl, RefitSettings? settings) { var client = CreateHttpClient(hostUrl, settings); @@ -54,8 +114,21 @@ public static object For(Type refitInterfaceType, string hostUrl, RefitSettings? return For(refitInterfaceType, client, settings); } + /// + /// Generate a Refit implementation of the specified interface. + /// + /// Interface to create the implementation for. + /// Base address the implementation will use. + /// An instance that implements . public static object For(Type refitInterfaceType, string hostUrl) => For(refitInterfaceType, hostUrl, null); + /// + /// Create an with as the base address. + /// + /// Base address. + /// to use to configure the HttpClient. + /// A with the various parameters provided. + /// public static HttpClient CreateHttpClient(string hostUrl, RefitSettings? settings) { if (string.IsNullOrWhiteSpace(hostUrl)) From 995ff886b64c23c770b5131c5049669a9ea700b9 Mon Sep 17 00:00:00 2001 From: Laurent Keusch <25180858+laurentksh@users.noreply.github.com> Date: Wed, 10 Nov 2021 17:10:58 +0100 Subject: [PATCH 2/3] -Added a comment to 1 attribute. -Added comments to MultipartItems. -Added comments to Refit settings. --- Refit/Attributes.cs | 3 +++ Refit/MultipartItem.cs | 9 +++++++++ Refit/RefitSettings.cs | 43 ++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 53 insertions(+), 2 deletions(-) diff --git a/Refit/Attributes.cs b/Refit/Attributes.cs index 6236a5881..0297323e6 100644 --- a/Refit/Attributes.cs +++ b/Refit/Attributes.cs @@ -135,6 +135,9 @@ public MultipartAttribute(string boundaryText = "----MyGreatBoundary") } + /// + /// Defines methods to serialize HTTP requests' bodies. + /// public enum BodySerializationMethod { /// diff --git a/Refit/MultipartItem.cs b/Refit/MultipartItem.cs index 00ce58ad0..fcb7c090a 100644 --- a/Refit/MultipartItem.cs +++ b/Refit/MultipartItem.cs @@ -38,6 +38,9 @@ public HttpContent ToContent() protected abstract HttpContent CreateContent(); } + /// + /// Allows the use of a generic in a multipart form body. + /// public class StreamPart : MultipartItem { public StreamPart(Stream value, string fileName, string? contentType = null, string? name = null) : @@ -54,6 +57,9 @@ protected override HttpContent CreateContent() } } + /// + /// Allows the use of a array in a multipart form body. + /// public class ByteArrayPart : MultipartItem { public ByteArrayPart(byte[] value, string fileName, string? contentType = null, string? name = null) : @@ -70,6 +76,9 @@ protected override HttpContent CreateContent() } } + /// + /// Allows the use of a object in a multipart form body. + /// public class FileInfoPart : MultipartItem { public FileInfoPart(FileInfo value, string fileName, string? contentType = null, string? name = null) : diff --git a/Refit/RefitSettings.cs b/Refit/RefitSettings.cs index 3c46e9776..82d36f2db 100644 --- a/Refit/RefitSettings.cs +++ b/Refit/RefitSettings.cs @@ -10,9 +10,11 @@ namespace Refit { + /// + /// Defines various parameters on how Refit should work. + /// public class RefitSettings - { - + { /// /// Creates a new instance with the default parameters /// @@ -62,13 +64,35 @@ public RefitSettings( /// public Func> ExceptionFactory { get; set; } + /// + /// Defines how requests' content should be serialized. (defaults to ) + /// public IHttpContentSerializer ContentSerializer { get; set; } + + /// + /// The instance to use (defaults to ) + /// public IUrlParameterFormatter UrlParameterFormatter { get; set; } + + /// + /// The instance to use (defaults to ) + /// public IFormUrlEncodedParameterFormatter FormUrlEncodedParameterFormatter { get; set; } + + /// + /// Sets the default collection format to use. (defaults to ) + /// public CollectionFormat CollectionFormat { get; set; } = CollectionFormat.RefitParameterFormatter; + + /// + /// Sets the default behavior when sending a request's body content. (defaults to false, request body is not streamed to the server) + /// public bool Buffered { get; set; } = false; } + /// + /// Provides content serialization to . + /// public interface IHttpContentSerializer { HttpContent ToHttpContent(T item); @@ -83,16 +107,25 @@ public interface IHttpContentSerializer string? GetFieldNameForProperty(PropertyInfo propertyInfo); } + /// + /// Provides Url parameter formatting. + /// public interface IUrlParameterFormatter { string? Format(object? value, ICustomAttributeProvider attributeProvider, Type type); } + /// + /// Provides form Url-encoded parameter formatting. + /// public interface IFormUrlEncodedParameterFormatter { string? Format(object? value, string? formatString); } + /// + /// Default Url parameter formater. + /// public class DefaultUrlParameterFormatter : IUrlParameterFormatter { static readonly ConcurrentDictionary> EnumMemberCache = new(); @@ -128,6 +161,9 @@ public class DefaultUrlParameterFormatter : IUrlParameterFormatter } } + /// + /// Default form Url-encoded parameter formatter. + /// public class DefaultFormUrlEncodedParameterFormatter : IFormUrlEncodedParameterFormatter { static readonly ConcurrentDictionary> EnumMemberCache @@ -155,6 +191,9 @@ public class DefaultFormUrlEncodedParameterFormatter : IFormUrlEncodedParameterF } } + /// + /// Default Api exception factory. + /// public class DefaultApiExceptionFactory { static readonly Task NullTask = Task.FromResult(null); From 1a839b80d124093bbffdeec5f914c13f918a0d50 Mon Sep 17 00:00:00 2001 From: Laurent Keusch <25180858+laurentksh@users.noreply.github.com> Date: Wed, 10 Nov 2021 17:39:24 +0100 Subject: [PATCH 3/3] -Added comments for the XmlContentSerializer. -Added comments to the IHttpContentSerializer interface. --- Refit/RefitSettings.cs | 17 +++++++++++++++-- Refit/XmlContentSerializer.cs | 19 ++++++++++++++++++- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/Refit/RefitSettings.cs b/Refit/RefitSettings.cs index 82d36f2db..3d6835838 100644 --- a/Refit/RefitSettings.cs +++ b/Refit/RefitSettings.cs @@ -95,15 +95,28 @@ public RefitSettings( /// public interface IHttpContentSerializer { + /// + /// Serializes an object of type to + /// + /// Type of the object to serialize from. + /// Object to serialize. + /// that contains the serialized object. HttpContent ToHttpContent(T item); + /// + /// Deserializes an object of type from an object. + /// + /// Type of the object to serialize to. + /// HttpContent object to deserialize. + /// CancellationToken to abort the deserialization. + /// The deserialized object of type . Task FromHttpContentAsync(HttpContent content, CancellationToken cancellationToken = default); /// /// Calculates what the field name should be for the given property. This may be affected by custom attributes the serializer understands /// - /// - /// + /// A PropertyInfo object. + /// The calculated field name. string? GetFieldNameForProperty(PropertyInfo propertyInfo); } diff --git a/Refit/XmlContentSerializer.cs b/Refit/XmlContentSerializer.cs index f219c4829..31e6d954d 100644 --- a/Refit/XmlContentSerializer.cs +++ b/Refit/XmlContentSerializer.cs @@ -12,7 +12,9 @@ namespace Refit { - + /// + /// A implementing which provides Xml content serialization. + /// public class XmlContentSerializer : IHttpContentSerializer { readonly XmlContentSerializerSettings settings; @@ -27,6 +29,13 @@ public XmlContentSerializer(XmlContentSerializerSettings settings) this.settings = settings ?? throw new ArgumentNullException(nameof(settings)); } + /// + /// Serialize object of type to a with Xml. + /// + /// Type of the object to serialize from. + /// Object to serialize. + /// that contains the serialized object in Xml. + /// public HttpContent ToHttpContent(T item) { if (item is null) throw new ArgumentNullException(nameof(item)); @@ -42,6 +51,13 @@ public HttpContent ToHttpContent(T item) return content; } + /// + /// Deserializes an object of type from a object that contains Xml content. + /// + /// Type of the object to deserialize to. + /// HttpContent object with Xml content to deserialize. + /// CancellationToken to abort the deserialization. + /// The deserialized object of type . public async Task FromHttpContentAsync(HttpContent content, CancellationToken cancellationToken = default) { var xmlSerializer = serializerCache.GetOrAdd(typeof(T), t => new XmlSerializer( @@ -58,6 +74,7 @@ public HttpContent ToHttpContent(T item) return (T?)xmlSerializer.Deserialize(reader); } + /// public string? GetFieldNameForProperty(PropertyInfo propertyInfo) { if (propertyInfo is null)