diff --git a/CHANGELOG.md b/CHANGELOG.md
index 89b40b9..9bfe00f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,15 +2,21 @@
[_vNext_](https://github.com/sketch7/FluentlyHttpClient/compare/3.8.1...3.9.0) (2020-X-X)
-## [4.0.0](https://github.com/sketch7/FluentlyHttpClient/compare/3.9.6...4.0.0) (2024-07-23)
+## [4.0.0](https://github.com/sketch7/FluentlyHttpClient/compare/3.9.6...4.0.0) (2024-07-24)
### Features
- **http client builder:** configurable http version/policy via `WithVersion`, `WithVersionPolicy`
- **http client builder:** defaults to http version http2.0
+### Performance
+- **logging:** middleware loggings changed to compile-time logging
+- **request builder:** uri interpolation regex compile-time
+
### BREAKING CHANGES
- **deps:** now target .net8
+- **http request:** remove deprecated `FluentHttpRequest.Formatters`
+- **models:** change several options/context models to `record`'s
## [3.9.6](https://github.com/sketch7/FluentlyHttpClient/compare/3.9.5...3.9.6) (2024-06-11)
diff --git a/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs b/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs
index 6e2a152..70487ed 100644
--- a/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs
+++ b/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs
@@ -26,7 +26,7 @@ public class HttpResponseSerializer : IHttpResponseSerializer
Hash = response.GetRequestHash(),
ReasonPhrase = response.ReasonPhrase,
StatusCode = (int)response.StatusCode,
- Url = response.Message.RequestMessage.RequestUri.ToString(),
+ Url = response.Message.RequestMessage!.RequestUri!.ToString(),
Version = response.Message.Version.ToString(),
Headers = new(response.Headers),
RequestMessage = response.Message.RequestMessage
@@ -43,18 +43,18 @@ public class HttpResponseSerializer : IHttpResponseSerializer
///
public Task Deserialize(IHttpResponseStore item)
{
- var contentType = new ContentType(item.ContentHeaders.ContentType);
+ var contentType = new ContentType(item.ContentHeaders!.ContentType!);
var encoding = string.IsNullOrEmpty(contentType.CharSet) ? Encoding.UTF8 : Encoding.GetEncoding(contentType.CharSet);
var cloned = new FluentHttpResponse(new((HttpStatusCode)item.StatusCode)
{
- Content = new StringContent(item.Content, encoding, contentType.MediaType),
+ Content = new StringContent(item.Content!, encoding, contentType.MediaType),
ReasonPhrase = item.ReasonPhrase,
- Version = new(item.Version),
+ Version = new(item.Version!),
RequestMessage = item.RequestMessage,
}); // todo: add items?
- cloned.Headers.AddRange(item.Headers);
+ cloned.Headers.AddRange(item.Headers!);
return Task.FromResult(cloned);
}
diff --git a/src/FluentlyHttpClient/FluentHttpClient.cs b/src/FluentlyHttpClient/FluentHttpClient.cs
index a4db5c2..713fc0d 100644
--- a/src/FluentlyHttpClient/FluentHttpClient.cs
+++ b/src/FluentlyHttpClient/FluentHttpClient.cs
@@ -36,7 +36,7 @@ public interface IFluentHttpClient : IDisposable
///
/// Gets the default formatter to be used when serializing body content. e.g. JSON, XML, etc...
///
- MediaTypeFormatter DefaultFormatter { get; }
+ MediaTypeFormatter? DefaultFormatter { get; }
/// Get the formatter for an HTTP content type.
/// The HTTP content type (or null to automatically select one).
@@ -102,7 +102,7 @@ public class FluentHttpClient : IFluentHttpClient
public MediaTypeFormatterCollection Formatters { get; }
///
- public MediaTypeFormatter DefaultFormatter { get; }
+ public MediaTypeFormatter? DefaultFormatter { get; }
///
public HttpRequestHeaders Headers { get; }
@@ -175,7 +175,7 @@ public FluentHttpRequestBuilder CreateRequest(string? uriTemplate = null, object
///
public async Task Send(FluentHttpRequest request)
{
- if (request == null) throw new ArgumentNullException(nameof(request));
+ ArgumentNullException.ThrowIfNull(request, nameof(request));
var requestId = request.Message.AddRequestId();
@@ -193,7 +193,7 @@ public async Task Send(FluentHttpRequest request)
public async Task Send(HttpRequestMessage request)
{
- if (request == null) throw new ArgumentNullException(nameof(request));
+ ArgumentNullException.ThrowIfNull(request, nameof(request));
var requestId = request.AddRequestId();
await RawHttpClient.SendAsync(request);
diff --git a/src/FluentlyHttpClient/FluentHttpClientBuilder.cs b/src/FluentlyHttpClient/FluentHttpClientBuilder.cs
index 45988ee..4aa9451 100644
--- a/src/FluentlyHttpClient/FluentHttpClientBuilder.cs
+++ b/src/FluentlyHttpClient/FluentHttpClientBuilder.cs
@@ -11,14 +11,14 @@ public class FluentHttpClientBuilder : IFluentHttpHeaderBuilder
/// Gets the identifier specified.
///
- public string? Identifier { get; private set; }
+ public string Identifier { get; private set; } = null!;
private readonly IServiceProvider _serviceProvider;
private readonly IFluentHttpClientFactory _fluentHttpClientFactory;
private readonly FluentHttpMiddlewareBuilder _middlewareBuilder;
private string? _baseUrl;
private TimeSpan _timeout;
- private readonly FluentHttpHeaders _headers = new();
+ private readonly FluentHttpHeaders _headers = [];
private Action? _requestBuilderDefaults;
private HttpMessageHandler? _httpMessageHandler;
private readonly FormatterOptions _formatterOptions = new();
@@ -73,42 +73,36 @@ public FluentHttpClientBuilder WithTimeout(TimeSpan timeout)
return this;
}
- ///
public FluentHttpClientBuilder WithHeader(string key, string value)
{
_headers.Set(key, value);
return this;
}
- ///
public FluentHttpClientBuilder WithHeader(string key, StringValues values)
{
_headers.Set(key, values);
return this;
}
- ///
public FluentHttpClientBuilder WithHeaders(IDictionary headers)
{
_headers.SetRange(headers);
return this;
}
- ///
public FluentHttpClientBuilder WithHeaders(IDictionary headers)
{
_headers.SetRange(headers);
return this;
}
- ///
public FluentHttpClientBuilder WithHeaders(IDictionary headers)
{
_headers.SetRange(headers);
return this;
}
- ///
public FluentHttpClientBuilder WithHeaders(FluentHttpHeaders headers)
{
_headers.SetRange(headers);
@@ -287,7 +281,7 @@ internal class MediaTypeFormatterComparer : IEqualityComparer x?.GetType() == y?.GetType();
+ public bool Equals(MediaTypeFormatter? x, MediaTypeFormatter? y) => x?.GetType() == y?.GetType();
public int GetHashCode(MediaTypeFormatter obj) => obj.GetType().GetHashCode();
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/FluentHttpClientOptions.cs b/src/FluentlyHttpClient/FluentHttpClientOptions.cs
index 6d78d32..f56b744 100644
--- a/src/FluentlyHttpClient/FluentHttpClientOptions.cs
+++ b/src/FluentlyHttpClient/FluentHttpClientOptions.cs
@@ -26,17 +26,17 @@ public class FluentHttpClientOptions
///
/// Gets or sets the identifier (key) for the HTTP client.
///
- public string? Identifier { get; set; }
+ public string Identifier { get; set; } = null!;
///
/// Gets or sets the headers which should be sent with each request.
///
- public FluentHttpHeaders? Headers { get; set; }
+ public FluentHttpHeaders Headers { get; set; } = null!;
///
/// Gets or sets the middleware builder.
///
- public FluentHttpMiddlewareBuilder MiddlewareBuilder { get; set; }
+ public FluentHttpMiddlewareBuilder MiddlewareBuilder { get; set; } = null!;
///
/// Gets or sets handler to customize request on creation. In order to specify defaults as desired, or so.
@@ -51,7 +51,7 @@ public class FluentHttpClientOptions
///
/// Gets or sets formatters to be used for content negotiation, for "Accept" and body media formats. e.g. JSON, XML, etc...
///
- public MediaTypeFormatterCollection? Formatters { get; set; }
+ public MediaTypeFormatterCollection Formatters { get; set; } = null!;
///
/// Gets or sets the default formatter to be used for content negotiation body format. e.g. JSON, XML, etc...
@@ -75,7 +75,7 @@ public FormatterOptions()
///
/// Configure formatters to be used.
///
- public MediaTypeFormatterCollection Formatters { get; } = new();
+ public MediaTypeFormatterCollection Formatters { get; } = [];
///
/// Set default formatter to be used when serializing body content and the preferred "Accept".
diff --git a/src/FluentlyHttpClient/FluentHttpHeaders.cs b/src/FluentlyHttpClient/FluentHttpHeaders.cs
index a637c30..f00eb6e 100644
--- a/src/FluentlyHttpClient/FluentHttpHeaders.cs
+++ b/src/FluentlyHttpClient/FluentHttpHeaders.cs
@@ -6,7 +6,7 @@ namespace FluentlyHttpClient;
///
/// options.
///
-public class FluentHttpHeadersOptions
+public record FluentHttpHeadersOptions
{
///
/// Predicate function to exclude headers from being hashed in .
@@ -43,9 +43,9 @@ public partial class FluentHttpHeaders : IFluentHttpHeaderBuilder _data = new(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary _data = new(StringComparer.OrdinalIgnoreCase);
- public string[] this[string key]
+ public string[]? this[string key]
{
get => _data[key];
set => _data[key] = value;
@@ -122,7 +122,7 @@ public FluentHttpHeaders(HttpHeaders headers)
/// Header value to add.
public FluentHttpHeaders Add(string header, string value)
{
- _data.Add(header, new[] { value });
+ _data.Add(header, [value]);
return this;
}
@@ -189,7 +189,7 @@ public FluentHttpHeaders AddRange(IDictionary> heade
public FluentHttpHeaders AddRange(IDictionary headers)
{
foreach (var header in headers)
- _data.Add(header.Key, new[] { header.Value });
+ _data.Add(header.Key, [header.Value]);
return this;
}
@@ -240,7 +240,7 @@ public StringValues Get(string header)
/// Header value to add.
public FluentHttpHeaders Set(string header, string value)
{
- this[header] = new[] { value };
+ this[header] = [value];
return this;
}
@@ -374,11 +374,12 @@ public string ToHashString()
///
/// Converts to dictionary.
///
- public Dictionary ToDictionary() => _data;
+ public Dictionary ToDictionary() => _data;
FluentHttpHeaders IFluentHttpHeaderBuilder.WithHeader(string key, string value) => Add(key, value);
FluentHttpHeaders IFluentHttpHeaderBuilder.WithHeader(string key, StringValues values) => Add(key, values);
FluentHttpHeaders IFluentHttpHeaderBuilder.WithHeaders(IDictionary headers) => SetRange(headers);
+ FluentHttpHeaders IFluentHttpHeaderBuilder.WithHeaders(IDictionary headers) => SetRange(headers);
FluentHttpHeaders IFluentHttpHeaderBuilder.WithHeaders(IDictionary headers) => SetRange(headers);
FluentHttpHeaders IFluentHttpHeaderBuilder.WithHeaders(FluentHttpHeaders headers) => SetRange(headers);
}
@@ -407,43 +408,43 @@ public StringValues AcceptLanguage
///
/// Gets or sets the Authorization header.
///
- public string Authorization
+ public string? Authorization
{
get => Get(HeaderTypes.Authorization);
- set => this[HeaderTypes.Authorization] = new[] { value };
+ set => this[HeaderTypes.Authorization] = [value];
}
///
/// Gets or sets the Cache-Control header.
///
- public string CacheControl
+ public string? CacheControl
{
get => Get(HeaderTypes.CacheControl);
- set => this[HeaderTypes.CacheControl] = new[] { value };
+ set => this[HeaderTypes.CacheControl] = [value];
}
///
/// Gets or sets the Content-Type header.
///
- public string ContentType
+ public string? ContentType
{
get => Get(HeaderTypes.ContentType);
- set => this[HeaderTypes.ContentType] = new[] { value };
+ set => this[HeaderTypes.ContentType] = [value];
}
///
/// Gets or sets the User-Agent header.
///
- public string UserAgent
+ public string? UserAgent
{
get => Get(HeaderTypes.UserAgent);
- set => this[HeaderTypes.UserAgent] = new[] { value };
+ set => this[HeaderTypes.UserAgent] = [value];
}
///
/// Gets or sets the X-Forwarded-For header.
///
- public StringValues XForwardedFor
+ public StringValues? XForwardedFor
{
get => Get(HeaderTypes.XForwardedFor);
set => this[HeaderTypes.XForwardedFor] = value;
@@ -452,9 +453,9 @@ public StringValues XForwardedFor
///
/// Gets or sets the X-Forwarded-Host header.
///
- public string XForwardedHost
+ public string? XForwardedHost
{
get => Get(HeaderTypes.XForwardedHost);
- set => this[HeaderTypes.XForwardedHost] = new[] { value };
+ set => this[HeaderTypes.XForwardedHost] = [value];
}
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/FluentHttpMessageExtensions.cs b/src/FluentlyHttpClient/FluentHttpMessageExtensions.cs
index 63e6467..484a0f2 100644
--- a/src/FluentlyHttpClient/FluentHttpMessageExtensions.cs
+++ b/src/FluentlyHttpClient/FluentHttpMessageExtensions.cs
@@ -1,4 +1,4 @@
-using System.Text;
+using System.Text;
namespace FluentlyHttpClient;
@@ -14,12 +14,12 @@ public static class FluentHttpMessageExtensions
public static async Task Clone(this FluentHttpResponse response)
{
var contentString = await response.Content.ReadAsStringAsync();
- var contentType = response.Content.Headers.ContentType;
+ var contentType = response.Content.Headers.ContentType!;
var encoding = string.IsNullOrEmpty(contentType.CharSet) ? Encoding.UTF8 : Encoding.GetEncoding(contentType.CharSet);
var cloned = new FluentHttpResponse(new(response.StatusCode)
{
- Content = new StringContent(contentString, encoding, contentType.MediaType),
+ Content = new StringContent(contentString, encoding, contentType.MediaType!),
ReasonPhrase = response.ReasonPhrase,
Version = response.Message.Version,
RequestMessage = response.Message.RequestMessage,
diff --git a/src/FluentlyHttpClient/FluentHttpRequest.cs b/src/FluentlyHttpClient/FluentHttpRequest.cs
index a6be21d..97b1ad8 100644
--- a/src/FluentlyHttpClient/FluentHttpRequest.cs
+++ b/src/FluentlyHttpClient/FluentHttpRequest.cs
@@ -1,4 +1,4 @@
-namespace FluentlyHttpClient;
+namespace FluentlyHttpClient;
///
/// Fluent HTTP request, which wraps the and add additional features.
@@ -30,7 +30,7 @@ public HttpMethod Method
///
/// Gets or sets the for the HTTP request.
///
- public Uri Uri
+ public Uri? Uri
{
get => Message.RequestUri;
set => Message.RequestUri = value;
@@ -42,7 +42,7 @@ public Uri Uri
public HttpRequestHeaders Headers => Message.Headers;
///
- /// Determine whether has success status otherwise it will throw or not.
+ /// Determine whether it has success status otherwise it will throw or not.
///
public bool HasSuccessStatusOrThrow { get; set; }
@@ -54,12 +54,6 @@ public Uri Uri
///
public IDictionary
public static Version Version = typeof(FluentlyHttpClientMeta).GetTypeInfo()
- .Assembly.GetName().Version;
+ .Assembly.GetName().Version!;
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/FluentlyHttpClient.csproj.DotSettings b/src/FluentlyHttpClient/FluentlyHttpClient.csproj.DotSettings
new file mode 100644
index 0000000..a74a2d7
--- /dev/null
+++ b/src/FluentlyHttpClient/FluentlyHttpClient.csproj.DotSettings
@@ -0,0 +1,2 @@
+
+ True
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/HttpMessageExtensions.cs b/src/FluentlyHttpClient/HttpMessageExtensions.cs
index 9a0c222..3458656 100644
--- a/src/FluentlyHttpClient/HttpMessageExtensions.cs
+++ b/src/FluentlyHttpClient/HttpMessageExtensions.cs
@@ -1,4 +1,4 @@
-namespace FluentlyHttpClient;
+namespace FluentlyHttpClient;
internal static class HttpMessageExtensions
{
diff --git a/src/FluentlyHttpClient/IFluentHttpHeaderBuilder.cs b/src/FluentlyHttpClient/IFluentHttpHeaderBuilder.cs
index c85c5eb..a56edaa 100644
--- a/src/FluentlyHttpClient/IFluentHttpHeaderBuilder.cs
+++ b/src/FluentlyHttpClient/IFluentHttpHeaderBuilder.cs
@@ -32,18 +32,13 @@ public interface IFluentHttpHeaderBuilder
/// Returns client builder for chaining.
T WithHeaders(IDictionary headers);
- ///
- /// Add the specified headers and their value for each request.
- ///
- /// Headers to add.
- /// Returns client builder for chaining.
+ ///
+ T WithHeaders(IDictionary headers);
+
+ ///
T WithHeaders(IDictionary headers);
- ///
- /// Add the specified headers and their value for each request.
- ///
- /// Headers to add.
- /// Returns client builder for chaining.
+ ///
T WithHeaders(FluentHttpHeaders headers);
}
diff --git a/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareBuilder.cs b/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareBuilder.cs
index 2f6be7a..ef93939 100644
--- a/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareBuilder.cs
+++ b/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareBuilder.cs
@@ -6,7 +6,7 @@ namespace FluentlyHttpClient.Middleware;
public class FluentHttpMiddlewareBuilder
{
private readonly IServiceProvider _serviceProvider;
- private readonly List _middleware = new();
+ private readonly List _middleware = [];
///
/// Gets the middleware count.
@@ -82,7 +82,7 @@ public IFluentHttpMiddlewareRunner Build(IFluentHttpClient httpClient)
{
FluentHttpMiddlewareDelegate next = previous!.Invoke;
if (pipe.Args == null)
- ctor = new object[] { next, clientContext };
+ ctor = [next, clientContext];
else
{
const int additionalCtorArgs = 2;
diff --git a/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareConfig.cs b/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareConfig.cs
index d9590de..352a72a 100644
--- a/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareConfig.cs
+++ b/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareConfig.cs
@@ -1,44 +1,13 @@
namespace FluentlyHttpClient.Middleware;
///
-/// Middleware configuration.
+/// HTTP Middleware configuration.
///
-[DebuggerDisplay("{DebuggerDisplay,nq}")]
-public class FluentHttpMiddlewareConfig
-{
- ///
- /// Debugger display.
- ///
- protected string DebuggerDisplay => $"Type: '{Type}', Args: '{Args}'";
-
- ///
- /// Gets or sets the type for the middleware.
- ///
- public Type Type { get; set; }
-
- ///
- /// Gets or sets the arguments for the middleware.
- ///
- public object[]? Args { get; set; }
-
- ///
- /// Initializes a new instance.
- ///
- public FluentHttpMiddlewareConfig()
- {
- }
-
- ///
- /// Initializes a new instance.
- ///
- public FluentHttpMiddlewareConfig(Type type, object[]? args = null)
- {
- Type = type;
- Args = args;
- }
-
- ///
- /// Destructuring.
- ///
- public void Deconstruct(out Type type, out object[] args) { type = Type; args = Args; }
-}
\ No newline at end of file
+///
+/// Type for the middleware
+/// Arguments for the middleware
+public record FluentHttpMiddlewareConfig(
+
+ Type Type,
+ object[]? Args = null
+);
diff --git a/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareRunner.cs b/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareRunner.cs
index 85d455b..b0ea40f 100644
--- a/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareRunner.cs
+++ b/src/FluentlyHttpClient/Middleware/FluentHttpMiddlewareRunner.cs
@@ -1,10 +1,10 @@
-namespace FluentlyHttpClient.Middleware;
+namespace FluentlyHttpClient.Middleware;
///
/// Fluent HTTP middleware client context (per client).
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
-public class FluentHttpMiddlewareClientContext
+public record FluentHttpMiddlewareClientContext
{
///
/// Debugger display.
@@ -37,7 +37,7 @@ public FluentHttpMiddlewareClientContext(string identifier, MediaTypeFormatterCo
/// Fluent HTTP middleware execution invoke context (per invoke).
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
-public class FluentHttpMiddlewareContext
+public record FluentHttpMiddlewareContext
{
///
/// Debugger display.
diff --git a/src/FluentlyHttpClient/Middleware/LoggerHttpMiddleware.cs b/src/FluentlyHttpClient/Middleware/LoggerHttpMiddleware.cs
index b9720cd..c9dcc70 100644
--- a/src/FluentlyHttpClient/Middleware/LoggerHttpMiddleware.cs
+++ b/src/FluentlyHttpClient/Middleware/LoggerHttpMiddleware.cs
@@ -1,13 +1,15 @@
+using FluentlyHttpClient;
using FluentlyHttpClient.Internal;
using FluentlyHttpClient.Middleware;
using Microsoft.Extensions.Logging;
+using System.Net;
namespace FluentlyHttpClient.Middleware
{
///
/// Logger HTTP middleware options.
///
- public class LoggerHttpMiddlewareOptions
+ public record LoggerHttpMiddlewareOptions
{
///
/// Gets or sets whether the request log should be detailed e.g. include body. Note: This should only be enabled for development or as needed,
@@ -66,45 +68,30 @@ public async Task Invoke(FluentHttpMiddlewareContext context
&& !options.ShouldLogDetailedResponse.GetValueOrDefault(false))
{
response = await _next(context);
- _logger.LogInformation("HTTP request [{method}] {requestUrl} responded {statusCode:D} in {elapsed:n0}ms",
- request.Method,
- request.Uri,
- response.StatusCode,
- watch.GetElapsedTime().TotalMilliseconds
- );
+ _logger.LoggerHttp_CondensedRequest(request.Method, request.Uri!, response.StatusCode, watch.GetElapsedTime().TotalMilliseconds);
return response;
}
if (!(options.ShouldLogDetailedRequest ?? false))
- _logger.LogInformation("Pre-request... {request}", request);
+ _logger.LoggerHttp_Request(request);
else
{
string? requestContent = null;
if (request.Message.Content != null)
requestContent = await request.Message.Content.ReadAsStringAsync();
- _logger.LogInformation(
- "Pre-request... {request}\nHeaders: {headers}\nContent: {requestContent}",
- request,
- request.Headers.ToFormattedString(),
- requestContent
- );
+ _logger.LoggerHttp_RequestDetailed(request, request.Headers.ToFormattedString(), requestContent);
}
response = await _next(context);
var stopwatchElapsed = watch.GetElapsedTime();
if (response.Content == null || !(options.ShouldLogDetailedResponse ?? false))
{
- _logger.LogInformation("Post-request... {response} in {elapsed:n0}ms", response, stopwatchElapsed.TotalMilliseconds);
+ _logger.LoggerHttp_Response(response, stopwatchElapsed.TotalMilliseconds);
return response;
}
var responseContent = await response.Content.ReadAsStringAsync();
- _logger.LogInformation("Post-request... {response}\nHeaders: {headers}\nContent: {responseContent} in {elapsed:n0}ms",
- response,
- response.Headers.ToFormattedString(),
- responseContent,
- stopwatchElapsed.TotalMilliseconds
- );
+ _logger.LoggerHttp_ResponseDetailed(response, response.Headers.ToFormattedString(), responseContent, stopwatchElapsed.TotalMilliseconds);
return response;
}
}
@@ -131,11 +118,7 @@ public static FluentHttpRequestBuilder WithLoggingOptions(this FluentHttpRequest
return requestBuilder;
}
- ///
- /// Set logging options for the request.
- ///
- /// Request builder instance.
- /// Action to configure logging options.
+ ///
public static FluentHttpRequestBuilder WithLoggingOptions(this FluentHttpRequestBuilder requestBuilder, Action? configure)
{
var options = new LoggerHttpMiddlewareOptions();
@@ -169,9 +152,7 @@ public static FluentHttpRequestBuilder WithLoggingOptions(this FluentHttpRequest
public static FluentHttpClientBuilder UseLogging(this FluentHttpClientBuilder builder, LoggerHttpMiddlewareOptions? options = null)
=> builder.UseMiddleware(options ?? new LoggerHttpMiddlewareOptions());
- ///
- /// Use logger middleware which logs out going requests and incoming responses.
- ///
+ ///
/// Builder instance
/// Action to configure logging options.
public static FluentHttpClientBuilder UseLogging(this FluentHttpClientBuilder builder, Action? configure)
@@ -181,4 +162,22 @@ public static FluentHttpClientBuilder UseLogging(this FluentHttpClientBuilder bu
return builder.UseLogging(options);
}
}
+}
+
+internal static partial class LogExtensions
+{
+ [LoggerMessage(LogLevel.Information, "HTTP request [{method}] {requestUrl} responded {statusCode:D} in {elapsed:n0}ms")]
+ internal static partial void LoggerHttp_CondensedRequest(this ILogger logger, HttpMethod method, Uri requestUrl, HttpStatusCode statusCode, double elapsed);
+
+ [LoggerMessage(LogLevel.Information, "Pre - request... {request}")]
+ internal static partial void LoggerHttp_Request(this ILogger logger, FluentHttpRequest request);
+
+ [LoggerMessage(LogLevel.Information, "Pre-request... {request}\nHeaders: {headers}\nContent: {requestContent}")]
+ internal static partial void LoggerHttp_RequestDetailed(this ILogger logger, FluentHttpRequest request, string headers, string? requestContent);
+
+ [LoggerMessage(LogLevel.Information, "Post-request... {response} in {elapsed:n0}ms")]
+ internal static partial void LoggerHttp_Response(this ILogger logger, FluentHttpResponse response, double elapsed);
+
+ [LoggerMessage(LogLevel.Information, "Post-request... {response}\nHeaders: {headers}\nContent: {responseContent} in {elapsed:n0}ms")]
+ internal static partial void LoggerHttp_ResponseDetailed(this ILogger logger, FluentHttpResponse response, string headers, string? responseContent, double elapsed);
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/Middleware/TimerHttpMiddleware.cs b/src/FluentlyHttpClient/Middleware/TimerHttpMiddleware.cs
index 49cbaa1..572d13d 100644
--- a/src/FluentlyHttpClient/Middleware/TimerHttpMiddleware.cs
+++ b/src/FluentlyHttpClient/Middleware/TimerHttpMiddleware.cs
@@ -1,4 +1,5 @@
-using FluentlyHttpClient.Internal;
+using FluentlyHttpClient;
+using FluentlyHttpClient.Internal;
using FluentlyHttpClient.Middleware;
using Microsoft.Extensions.Logging;
@@ -7,7 +8,7 @@ namespace FluentlyHttpClient.Middleware
///
/// Timer HTTP middleware options.
///
- public class TimerHttpMiddlewareOptions
+ public record TimerHttpMiddlewareOptions
{
///
/// Gets or sets the threshold warning timespan in order to log as warning.
@@ -20,14 +21,10 @@ public class TimerHttpMiddlewareOptions
///
public class TimerHttpMiddleware : IFluentHttpMiddleware
{
- private const string TimeTakenMessage = "Executed request {request} in {elapsed:n0}ms";
private readonly FluentHttpMiddlewareDelegate _next;
private readonly TimerHttpMiddlewareOptions _options;
private readonly ILogger _logger;
- ///
- /// Initializes a new instance.
- ///
public TimerHttpMiddleware(
FluentHttpMiddlewareDelegate next,
FluentHttpMiddlewareClientContext context,
@@ -60,9 +57,9 @@ public async Task Invoke(FluentHttpMiddlewareContext context
{
var threshold = request.GetTimerWarnThreshold() ?? _options.WarnThreshold;
if (_logger.IsEnabled(LogLevel.Warning) && stopwatchElapsed > threshold)
- _logger.LogWarning(TimeTakenMessage, request, stopwatchElapsed.TotalMilliseconds);
+ _logger.TimerHttp_TimeTaken(LogLevel.Warning, request, stopwatchElapsed.TotalMilliseconds);
else if (_logger.IsEnabled(LogLevel.Debug))
- _logger.LogDebug(TimeTakenMessage, request, stopwatchElapsed.TotalMilliseconds);
+ _logger.TimerHttp_TimeTaken(LogLevel.Debug, request, stopwatchElapsed.TotalMilliseconds);
}
return response.SetTimeTaken(stopwatchElapsed);
@@ -134,9 +131,7 @@ public static TimeSpan GetTimeTaken(this FluentHttpResponse response)
public static FluentHttpClientBuilder UseTimer(this FluentHttpClientBuilder builder, TimerHttpMiddlewareOptions? options = null)
=> builder.UseMiddleware(options ?? new TimerHttpMiddlewareOptions());
- ///
- /// Use timer middleware which measures how long the request takes.
- ///
+ ///
/// Builder instance
/// Action to configure timer options.
public static FluentHttpClientBuilder UseTimer(this FluentHttpClientBuilder builder, Action? configure)
@@ -146,4 +141,10 @@ public static FluentHttpClientBuilder UseTimer(this FluentHttpClientBuilder buil
return builder.UseTimer(options);
}
}
+}
+
+internal static partial class LogExtensions
+{
+ [LoggerMessage("Executed request {request} in {elapsed:n0}ms")]
+ internal static partial void TimerHttp_TimeTaken(this ILogger logger, LogLevel level, FluentHttpRequest request, double elapsed);
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/RequestHashingExtensions.cs b/src/FluentlyHttpClient/RequestHashingExtensions.cs
index 4bbf8bc..65f32de 100644
--- a/src/FluentlyHttpClient/RequestHashingExtensions.cs
+++ b/src/FluentlyHttpClient/RequestHashingExtensions.cs
@@ -1,4 +1,4 @@
-using Newtonsoft.Json;
+using Newtonsoft.Json;
using System.Collections.Specialized;
namespace FluentlyHttpClient;
@@ -99,7 +99,7 @@ public static FluentHttpRequestBuilder WithRequestHashOptions(this FluentHttpReq
/// Get response caching options for the request.
///
/// Request to get options from.
- public static RequestHashOptions GetRequestHashOptions(this FluentHttpRequest request)
+ public static RequestHashOptions? GetRequestHashOptions(this FluentHttpRequest request)
{
request.Items.TryGetValue(HashOptionsKey, out var result);
return (RequestHashOptions)result;
diff --git a/src/FluentlyHttpClient/Utils/HttpExtensions.cs b/src/FluentlyHttpClient/Utils/HttpExtensions.cs
index 7bc5690..2206fa3 100644
--- a/src/FluentlyHttpClient/Utils/HttpExtensions.cs
+++ b/src/FluentlyHttpClient/Utils/HttpExtensions.cs
@@ -1,7 +1,6 @@
-using System.Collections.Specialized;
+using System.Collections.Specialized;
using System.Web;
-// ReSharper disable once CheckNamespace
namespace FluentlyHttpClient;
///
diff --git a/src/FluentlyHttpClient/Utils/ObjectExtensions.cs b/src/FluentlyHttpClient/Utils/ObjectExtensions.cs
index 198756c..ddc4350 100644
--- a/src/FluentlyHttpClient/Utils/ObjectExtensions.cs
+++ b/src/FluentlyHttpClient/Utils/ObjectExtensions.cs
@@ -1,8 +1,7 @@
-using System.Collections;
+using System.Collections;
using System.Reflection;
using System.Runtime.Serialization;
-// ReSharper disable once CheckNamespace
namespace FluentlyHttpClient;
///
diff --git a/src/FluentlyHttpClient/Utils/QueryStringOptions.cs b/src/FluentlyHttpClient/Utils/QueryStringOptions.cs
index a71fc96..6e92775 100644
--- a/src/FluentlyHttpClient/Utils/QueryStringOptions.cs
+++ b/src/FluentlyHttpClient/Utils/QueryStringOptions.cs
@@ -1,8 +1,5 @@
using System.Web;
-#pragma warning disable 618 // todo: remove after removing deprecated code
-
-// ReSharper disable once CheckNamespace
namespace FluentlyHttpClient;
///
@@ -25,7 +22,7 @@ public enum QueryStringCollectionMode
/// Querystring formatting options.
///
[DebuggerDisplay("{DebuggerDisplay,nq}")]
-public class QueryStringOptions
+public record QueryStringOptions
{
private static readonly Func CamelCaseString = key => key.ToCamelCase();
///
@@ -50,14 +47,12 @@ public class QueryStringOptions
///
/// Gets or sets the function to format a collection item. This will allow you to manipulate the value.
///
- [Obsolete("Use WithValueFormatter instead.")] // deprecated: remove
public Func