diff --git a/Directory.Build.props b/Directory.Build.props
index ca19c4e..d21a01d 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -28,8 +28,8 @@
- false
- $(NoWarn);1701;1702;1705;1591
+ true
+ $(NoWarn);1701;1702;1705true14.0enable
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 45763d3..d92e5be 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -14,7 +14,7 @@
-
+
@@ -26,7 +26,8 @@
-
+
+
diff --git a/README.md b/README.md
index 31df7ae..f227473 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,7 @@
[](https://github.com/sketch7/FluentlyHttpClient/actions/workflows/ci.yml)
[](https://badge.fury.io/nu/fluentlyhttpclient)
-Http Client for .NET Standard with fluent APIs which are intuitive, easy to use and also highly extensible.
+Http Client for .NET with fluent APIs which are intuitive, easy to use and also highly extensible.
**Quick links**
@@ -35,6 +35,7 @@ Http Client for .NET Standard with fluent APIs which are intuitive, easy to use
| 2.x | .NET Standard 2 | |
| 3.x | .NET Standard 2 | [](https://github.com/sketch7/FluentlyHttpClient/actions/workflows/ci.yml) |
| 4.x | net8 | [](https://github.com/sketch7/FluentlyHttpClient/actions/workflows/ci.yml) |
+| 5.x | net10 | [](https://github.com/sketch7/FluentlyHttpClient/actions/workflows/ci.yml) |
### NuGet
```
@@ -69,7 +70,7 @@ PM> Install-Package FluentlyHttpClient
- [Usage](#usage-1)
- [Query params](#query-params)
- [Interpolate Url](#interpolate-url)
- - [ReturnAsReponse, ReturnAsResponse`` and Return``](#returnasreponse-returnasresponset-and-returnt)
+ - [ReturnAsResponse, ReturnAsResponse`` and Return``](#returnasresponse-returnasresponset-and-returnt)
- [GraphQL](#graphql)
- [Middleware](#middleware)
- [Middleware options](#middleware-options)
@@ -84,9 +85,6 @@ PM> Install-Package FluentlyHttpClient
- [Simple Single file HttpClient](#simple-single-file-httpclient)
- [Testing/Mocking](#testingmocking)
- [Test example with RichardSzalay.MockHttp](#test-example-with-richardszalaymockhttp)
-- [Contributing](#contributing)
- - [Setup Machine for Development](#setup-machine-for-development)
- - [Commands](#commands)
## Usage
@@ -94,25 +92,21 @@ PM> Install-Package FluentlyHttpClient
Add services via `.AddFluentlyHttpClient()`.
```cs
-// using Startup.cs (can be elsewhere)
-public void ConfigureServices(IServiceCollection services)
-{
- services.AddFluentlyHttpClient();
-}
+// Program.cs
+var builder = WebApplication.CreateBuilder(args);
+builder.Services.AddFluentlyHttpClient();
+var app = builder.Build();
```
-Configure an Http client using the Http Factory (you need at least one).
+Configure an HTTP client using the HTTP client factory (you need at least one).
```cs
-// using Startup.cs (can be elsewhere)
-public void Configure(IApplicationBuilder app, IFluentHttpClientFactory fluentHttpClientFactory)
-{
- fluentHttpClientFactory.CreateBuilder(identifier: "platform") // keep a note of the identifier, its needed later
- .WithBaseUrl("http://sketch7.com") // required
- .WithHeader("user-agent", "slabs-testify")
- .WithTimeout(5)
- .UseMiddleware()
- .Register(); // register client builder to factory
-}
+var fluentHttpClientFactory = app.Services.GetRequiredService();
+fluentHttpClientFactory.CreateBuilder(identifier: "platform") // keep a note of the identifier, it's needed later
+ .WithBaseUrl("https://sketch7.com") // required
+ .WithHeader("user-agent", "my-app")
+ .WithTimeout(5)
+ .UseMiddleware()
+ .Register(); // register client builder to factory
```
### Basic usage
@@ -124,11 +118,11 @@ Simple API (non-fluent) is good for simple requests as it has a clean, minimal A
// inject factory and get client
var httpClient = fluentHttpClientFactory.Get(identifier: "platform");
-// HTTP GET + deserialize result (non-fleunt API)
-Hero hero = await httpClient.Get("/api/heroes/azmodan");
+// HTTP GET + deserialize result (non-fluent API)
+Hero? hero = await httpClient.Get("/api/heroes/azmodan");
-// HTTP POST + deserialize result (non-fleunt API)
-Hero hero = await httpClient.Post("/api/heroes/azmodan", new
+// HTTP POST + deserialize result (non-fluent API)
+Hero? hero = await httpClient.Post("/api/heroes/azmodan", new
{
Title = "Lord of Sin"
});
@@ -147,7 +141,7 @@ FluentHttpResponse response =
.ReturnAsResponse(); // return with response
// HTTP POST + return response and deserialize result (fluent API)
-Hero hero = await httpClient.CreateRequest("/api/heroes/azmodan")
+Hero? hero = await httpClient.CreateRequest("/api/heroes/azmodan")
.AsPost()
.WithBody(new
{
@@ -163,11 +157,11 @@ Http client builder is used to configure http clients in a fluent way.
```cs
var clientBuilder = fluentHttpClientFactory.CreateBuilder(identifier: "platform")
- .WithBaseUrl("http://sketch7.com");
+ .WithBaseUrl("https://sketch7.com");
fluentHttpClientFactory.Add(clientBuilder);
// or similarly via the builder itself.
-clientBuilder.Register().
+clientBuilder.Register();
```
#### Register multiple + share
@@ -237,7 +231,8 @@ httpClientBuilder.ConfigureFormatters(opts =>
opts.Formatters.Add(new CustomFormatter());
});
-httpClientBuilder.WithVersion(HttpVersion.Version30) // specify to use http3 (defaults: http2)
+// http version - configure per-request via request builder defaults
+httpClientBuilder.WithRequestBuilderDefaults(builder => builder.WithVersion(HttpVersion.Version30)); // specify http3 per-request (default: http2)
```
#### Re-using Http Client from Factory
@@ -259,7 +254,7 @@ Request builder is used to build http requests in a fluent way.
#### Usage
```cs
-LoginResponse loginResponse =
+LoginResponse? loginResponse =
await fluentHttpClient.CreateRequest("/api/auth/login")
.AsPost() // set as HTTP Post
.WithBody(new
@@ -292,7 +287,7 @@ requestBuilder.WithUri("{Language}/heroes/{Hero}", new
}); // => /en/heroes/azmodan
```
-#### ReturnAsReponse, ReturnAsResponse`` and Return``
+#### ReturnAsResponse, ReturnAsResponse`` and Return``
```cs
// send and returns HTTP response
@@ -301,8 +296,8 @@ FluentHttpResponse response = requestBuilder.ReturnAsResponse();
// send and returns HTTP response + deserialize and return result via `.Data`
FluentHttpResponse response = requestBuilder.ReturnAsResponse();
-// send and returns derserialized result directly
-Hero hero = requestBuilder.Return();
+// send and returns deserialized result directly
+Hero? hero = requestBuilder.Return();
```
@@ -317,7 +312,7 @@ httpClientBuilder.WithRequestBuilderDefaults(requestBuilder => requestBuilder.Wi
FluentHttpResponse response =
await fluentHttpClient.CreateGqlRequest("{ hero {name, title } }")
.ReturnAsGqlResponse();
- // => response.Data.Title
+ // => response.Data?.Title
```
@@ -334,9 +329,9 @@ These are provided out of the box:
| Timer | Determine how long (timespan) requests takes. |
| Logger | Log request/response. |
-Two important points to keep in mind:
+Three important points to keep in mind:
- The first argument within constructor has to be `FluentHttpMiddlewareDelegate` which is generally called `next`.
- - The second argument within constructor has to be `FluentHttpMiddlewareClientContext` which is generally called `context`,
+ - The second argument within constructor has to be `FluentHttpMiddlewareClientContext` which is generally called `context`.
- During `Invoke` the `await _next(context);` must be invoked and return the response, in order to continue the flow.
The following is the timer middleware implementation *(bit simplified)*.
@@ -388,7 +383,7 @@ namespace FluentlyHttpClient
// FluentHttpClientBuilder extension methods - add
public static class FluentlyHttpMiddlwareExtensions
{
- public static FluentHttpClientBuilder UseTimer(this FluentHttpClientBuilder builder, TimerHttpMiddlewareOptions options = null)
+ public static FluentHttpClientBuilder UseTimer(this FluentHttpClientBuilder builder, TimerHttpMiddlewareOptions? options = null)
=> builder.UseMiddleware(options ?? new TimerHttpMiddlewareOptions());
}
}
@@ -398,7 +393,7 @@ TimeSpan timeTaken = response.GetTimeTaken();
```
#### Middleware options
-Options to middleware can be passed via an argument. Note it has to be the second argument within the constructor.
+Options to middleware can be passed via an argument. Note it has to be the third argument within the constructor (after `next` and `context`).
```cs
public TimerHttpMiddleware(
@@ -482,13 +477,14 @@ public static class FluentHttpHeaderBuilderExtensions
builder.WithHeader(HeaderTypes.Authorization, $"{AuthSchemeTypes.Bearer} {token}");
return (T)builder;
}
+}
```
#### Extending Request/Response items
In order to extend `Items` for both `FluentHttpRequest` and `FluentHttpResponse`, its best to extend `IFluentHttpMessageState`.
This way it will be available for both. See example below.
```cs
-public static IDictionary GetErrorCodeMappings(this IFluentHttpMessageState message)
+public static IDictionary? GetErrorCodeMappings(this IFluentHttpMessageState message)
{
if (message.Items.TryGetValue(ErrorCodeMappingKey, out var value))
return (IDictionary)value;
@@ -526,7 +522,7 @@ public class SelfInfoHttpClient
)
{
_httpClient = httpClientFactory.CreateBuilder("localhost")
- .WithBaseUrl($"http://localhost:5500}")
+ .WithBaseUrl("http://localhost:5500")
.Build();
}
@@ -545,14 +541,14 @@ However, we've been using [RichardSzalay.MockHttp](https://github.com/richardsza
```cs
[Fact]
-public async void ShouldReturnContent()
+public async Task ShouldReturnContent()
{
// build services
var servicesProvider = new ServiceCollection()
.AddFluentlyHttpClient()
.AddLogging()
.BuildServiceProvider();
- var fluentHttpClientFactory = servicesProvider.GetService();
+ var fluentHttpClientFactory = servicesProvider.GetRequiredService();
// define mocks
var mockHttp = new MockHttpMessageHandler();
@@ -561,7 +557,7 @@ public async void ShouldReturnContent()
var httpClient = fluentHttpClientFactory.CreateBuilder("platform")
.WithBaseUrl("https://sketch7.com")
- .AddMiddleware()
+ .UseMiddleware()
.WithMessageHandler(mockHttp) // set message handler to mock
.Build();
@@ -573,28 +569,3 @@ public async void ShouldReturnContent()
Assert.NotEqual(TimeSpan.Zero, response.GetTimeTaken());
}
```
-
-## Contributing
-
-### Setup Machine for Development
-Install/setup the following:
-
-- NodeJS v8+
-- Visual Studio Code or similar code editor
-- Git + SourceTree, SmartGit or similar (optional)
-
- ### Commands
-
-```bash
-# run tests
-npm test
-
-# bump version
-npm version minor --no-git-tag # major | minor | patch | prerelease
-
-# nuget pack (only)
-npm run pack
-
-# nuget publish dev (pack + publish + clean)
-npm run publish:dev
-```
\ No newline at end of file
diff --git a/benchmark/Benchmarking.cs b/benchmark/Benchmarking.cs
index 83d26c5..2489306 100644
--- a/benchmark/Benchmarking.cs
+++ b/benchmark/Benchmarking.cs
@@ -44,11 +44,11 @@ public void Setup()
{
var mockHttp = new MockHttpMessageHandler();
mockHttp.When(HttpMethod.Post, "https://sketch7.com/api/json")
- .Respond("application/json", request => request.Content.ReadAsStreamAsync().Result);
+ .Respond("application/json", request => request.Content!.ReadAsStreamAsync().Result);
mockHttp.When(HttpMethod.Post, "https://sketch7.com/api/msgpack")
- //.Respond("application/x-msgpack", "��Key�valeera�Name�Valeera�Title�Shadow of the Uncrowned")
- .Respond("application/x-msgpack", request => request.Content.ReadAsStreamAsync().Result);
+ //.Respond("application/x-msgpack", "\x99\xa3Key\xa6valeera...")
+ .Respond("application/x-msgpack", request => request.Content!.ReadAsStreamAsync().Result);
var fluentHttpClientFactory = BuildContainer()
.GetRequiredService();
@@ -79,33 +79,33 @@ public void Setup()
;
Console.WriteLine($"Setup Complete");
- Console.WriteLine($" - _jsonHttpClient: {_jsonHttpClient.DefaultFormatter.GetType().Name}");
- Console.WriteLine($" - _messagePackHttpClient: {_messagePackHttpClient.DefaultFormatter.GetType().Name}");
- Console.WriteLine($" - _systemTextJsonHttpClient: {_systemTextJsonHttpClient.DefaultFormatter.GetType().Name}");
+ Console.WriteLine($" - _jsonHttpClient: {_jsonHttpClient!.DefaultFormatter?.GetType().Name}");
+ Console.WriteLine($" - _messagePackHttpClient: {_messagePackHttpClient!.DefaultFormatter?.GetType().Name}");
+ Console.WriteLine($" - _systemTextJsonHttpClient: {_systemTextJsonHttpClient!.DefaultFormatter?.GetType().Name}");
}
[Benchmark]
- public Task PostAsJson()
+ public Task PostAsJson()
{
- return _jsonHttpClient.CreateRequest("/api/json")
+ return _jsonHttpClient!.CreateRequest("/api/json")
.AsPost()
.WithBody(_request)
.Return();
}
[Benchmark]
- public Task PostAsMessagePack()
+ public Task PostAsMessagePack()
{
- return _messagePackHttpClient.CreateRequest("/api/msgpack")
+ return _messagePackHttpClient!.CreateRequest("/api/msgpack")
.AsPost()
.WithBody(_request)
.Return();
}
[Benchmark]
- public Task PostAsSystemTextJson()
+ public Task PostAsSystemTextJson()
{
- return _systemTextJsonHttpClient.CreateRequest("/api/json")
+ return _systemTextJsonHttpClient!.CreateRequest("/api/json")
.AsPost()
.WithBody(_request)
.Return();
diff --git a/benchmark/FluentlyHttpClient.Benchmarks.csproj b/benchmark/FluentlyHttpClient.Benchmarks.csproj
index 44beddd..60e5443 100644
--- a/benchmark/FluentlyHttpClient.Benchmarks.csproj
+++ b/benchmark/FluentlyHttpClient.Benchmarks.csproj
@@ -5,6 +5,7 @@
enableenablefalse
+ $(NoWarn);1591
diff --git a/package.json b/package.json
index 56b6df8..b6136b2 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@sketch7/fluently-http-client",
- "version": "4.1.2",
+ "version": "5.0.0",
"scripts": {
"pack": "bash ./tools/pack.sh",
"prepublish:dev": "npm run pack",
diff --git a/samples/FluentlyHttpClient.Sample.Api/FluentlyHttpClient.Sample.Api.csproj b/samples/FluentlyHttpClient.Sample.Api/FluentlyHttpClient.Sample.Api.csproj
index 3df8084..76f76a3 100644
--- a/samples/FluentlyHttpClient.Sample.Api/FluentlyHttpClient.Sample.Api.csproj
+++ b/samples/FluentlyHttpClient.Sample.Api/FluentlyHttpClient.Sample.Api.csproj
@@ -1,5 +1,9 @@
+
+ $(NoWarn);1591
+
+
diff --git a/samples/FluentlyHttpClient.Sample.Api/Heroes/Hero.cs b/samples/FluentlyHttpClient.Sample.Api/Heroes/Hero.cs
index d440273..2d76bf6 100644
--- a/samples/FluentlyHttpClient.Sample.Api/Heroes/Hero.cs
+++ b/samples/FluentlyHttpClient.Sample.Api/Heroes/Hero.cs
@@ -5,9 +5,9 @@ namespace FluentlyHttpClient.Sample.Api.Heroes;
public record Hero
{
[Required]
- public string Key { get; set; }
+ public required string Key { get; init; }
[Required]
- public string Name { get; set; }
- public string Title { get; set; }
+ public required string Name { get; init; }
+ public string? Title { get; init; }
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient.Entity/Constants.cs b/src/FluentlyHttpClient.Entity/Constants.cs
index 88e0c23..aadf269 100644
--- a/src/FluentlyHttpClient.Entity/Constants.cs
+++ b/src/FluentlyHttpClient.Entity/Constants.cs
@@ -1,12 +1,18 @@
namespace FluentlyHttpClient.Entity;
+/// Internal constants used for EF Core schema configuration.
public static class Constants
{
+ /// Max length for short text columns (30).
public const int ShortTextLength = 30;
+ /// Max length for normal text columns (70).
public const int NormalTextLength = 70;
+ /// Max length for long text columns (1500).
public const int LongTextLength = 1500;
+ /// SQL schema name for cache tables.
public const string SchemaName = "cache";
+ /// Table name for HTTP response cache entries.
public const string HttpResponseTable = "HttpResponses";
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient.Entity/DataSerializer.cs b/src/FluentlyHttpClient.Entity/DataSerializer.cs
index f5bb519..5e36c2b 100644
--- a/src/FluentlyHttpClient.Entity/DataSerializer.cs
+++ b/src/FluentlyHttpClient.Entity/DataSerializer.cs
@@ -3,6 +3,7 @@
namespace FluentlyHttpClient.Entity;
+/// JSON serialization helpers for EF Core value conversions.
public static class DataSerializer
{
private static readonly JsonSerializerSettings Settings = new()
@@ -10,6 +11,10 @@ public static class DataSerializer
ContractResolver = new DefaultContractResolver()
};
+ /// Serialize to a JSON string.
public static string Serialize(TItem value) => JsonConvert.SerializeObject(value, Settings);
- public static TResult Deserialize(string value) => JsonConvert.DeserializeObject(value, Settings);
+ /// Deserialize a JSON string to .
+ public static TResult Deserialize(string value) =>
+ // JsonConvert.DeserializeObject returns non-null when given a valid JSON string and valid target type
+ JsonConvert.DeserializeObject(value, Settings)!;
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient.Entity/FluentHttpClientDbContext.cs b/src/FluentlyHttpClient.Entity/FluentHttpClientDbContext.cs
index c8dfefc..a77237d 100644
--- a/src/FluentlyHttpClient.Entity/FluentHttpClientDbContext.cs
+++ b/src/FluentlyHttpClient.Entity/FluentHttpClientDbContext.cs
@@ -3,14 +3,18 @@
namespace FluentlyHttpClient.Entity;
+/// EF Core database context for the HTTP response cache store.
public class FluentHttpClientDbContext : DbContext
{
+ /// Initializes a new instance of .
public FluentHttpClientDbContext(DbContextOptions options)
: base(options)
{ }
+ /// Gets the data set.
public DbSet HttpResponses { get; set; } = null!;
+ ///
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
@@ -18,7 +22,9 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());
}
+ /// Apply pending EF Core migrations idempotently.
public Task Initialize() => Database.MigrateAsync();
+ /// Persist pending changes to the database.
public Task Commit() => SaveChangesAsync();
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient.Entity/FluentHttpHeadersConverter.cs b/src/FluentlyHttpClient.Entity/FluentHttpHeadersConverter.cs
index 00797ba..55180be 100644
--- a/src/FluentlyHttpClient.Entity/FluentHttpHeadersConverter.cs
+++ b/src/FluentlyHttpClient.Entity/FluentHttpHeadersConverter.cs
@@ -2,10 +2,12 @@
namespace FluentlyHttpClient.Entity;
+/// EF Core value conversion helpers for .
public static class FluentHttpHeadersConversion
{
- public static ValueConverter Convert = new(
- x => DataSerializer.Serialize(x),
+ /// Converter that serializes to/from a JSON string.
+ public static ValueConverter Convert = new(
+ x => x != null ? DataSerializer.Serialize(x) : string.Empty,
x => DataSerializer.Deserialize(x)
);
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient.Entity/HttpResponseEntity.cs b/src/FluentlyHttpClient.Entity/HttpResponseEntity.cs
index fd37b40..484f3c0 100644
--- a/src/FluentlyHttpClient.Entity/HttpResponseEntity.cs
+++ b/src/FluentlyHttpClient.Entity/HttpResponseEntity.cs
@@ -2,17 +2,29 @@
namespace FluentlyHttpClient.Entity;
+/// EF Core entity representing a cached HTTP response.
public class HttpResponseEntity : IHttpResponseStore
{
+ /// Gets or sets the primary key (hashed request identifier).
public string? Id { get; set; }
+ ///
public string? Name { get; set; }
+ ///
public string? Hash { get; set; }
+ ///
public string? Url { get; set; }
+ ///
public string? Content { get; set; }
+ ///
public FluentHttpHeaders? Headers { get; set; }
+ ///
public int StatusCode { get; set; }
+ ///
public string? ReasonPhrase { get; set; }
+ ///
public string? Version { get; set; }
+ ///
public FluentHttpHeaders? ContentHeaders { get; set; }
+ ///
public HttpRequestMessage? RequestMessage { get; set; }
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient.Entity/HttpResponseMapping.cs b/src/FluentlyHttpClient.Entity/HttpResponseMapping.cs
index 5687b91..76480c1 100644
--- a/src/FluentlyHttpClient.Entity/HttpResponseMapping.cs
+++ b/src/FluentlyHttpClient.Entity/HttpResponseMapping.cs
@@ -3,8 +3,10 @@
namespace FluentlyHttpClient.Entity;
+/// EF Core mapping configuration for .
public class HttpResponseMapping : IEntityTypeConfiguration
{
+ ///
public void Configure(EntityTypeBuilder builder)
{
builder.ToTable(Constants.HttpResponseTable, Constants.SchemaName);
diff --git a/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.Designer.cs b/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.Designer.cs
index f7eaf37..91e6044 100644
--- a/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.Designer.cs
+++ b/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.Designer.cs
@@ -1,4 +1,5 @@
//
+#pragma warning disable CS1591
using FluentlyHttpClient.Entity;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Infrastructure;
@@ -63,7 +64,7 @@ protected override void BuildTargetModel(ModelBuilder modelBuilder)
b.HasKey("Id");
- b.ToTable("HttpResponses","cache");
+ b.ToTable("HttpResponses", "cache");
});
#pragma warning restore 612, 618
}
diff --git a/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.cs b/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.cs
index 50494f1..242f012 100644
--- a/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.cs
+++ b/src/FluentlyHttpClient.Entity/Migrations/20190315104244_Initial.cs
@@ -1,4 +1,5 @@
-using Microsoft.EntityFrameworkCore.Migrations;
+#pragma warning disable CS1591
+using Microsoft.EntityFrameworkCore.Migrations;
namespace FluentlyHttpClient.Entity.Migrations
{
diff --git a/src/FluentlyHttpClient.Entity/RemoteResponseCacheService.cs b/src/FluentlyHttpClient.Entity/RemoteResponseCacheService.cs
index f7a687e..e7a23b3 100644
--- a/src/FluentlyHttpClient.Entity/RemoteResponseCacheService.cs
+++ b/src/FluentlyHttpClient.Entity/RemoteResponseCacheService.cs
@@ -4,12 +4,14 @@
namespace FluentlyHttpClient.Entity;
+/// SQL Server-backed implementation of using EF Core with a local memory cache layer.
public class RemoteResponseCacheService : IResponseCacheService
{
private readonly FluentHttpClientDbContext _dbContext;
private readonly IHttpResponseSerializer _serializer;
private readonly IMemoryCache _cache;
+ /// Initializes a new instance of .
public RemoteResponseCacheService(
FluentHttpClientDbContext dbContext,
IHttpResponseSerializer serializer,
@@ -21,10 +23,11 @@ IMemoryCache cache
_cache = cache;
}
+ ///
public async Task Get(string hash)
{
var id = await hash.ComputeHash();
- var result = await _cache.GetOrCreate(id, async _ =>
+ var cachedTask = _cache.GetOrCreate(id, async _ =>
{
var item = await _dbContext.HttpResponses.FindAsync(id);
@@ -32,10 +35,11 @@ IMemoryCache cache
return await _serializer.Deserialize(item);
});
-
+ var result = cachedTask != null ? await cachedTask : null;
return result != null ? await result.Clone() : null;
}
+ ///
public async Task Set(string hash, FluentHttpResponse response)
{
var item = await _serializer.Serialize(response);
diff --git a/src/FluentlyHttpClient.Entity/ServiceCollectionExtensions.cs b/src/FluentlyHttpClient.Entity/ServiceCollectionExtensions.cs
index 8e45bf4..6584758 100644
--- a/src/FluentlyHttpClient.Entity/ServiceCollectionExtensions.cs
+++ b/src/FluentlyHttpClient.Entity/ServiceCollectionExtensions.cs
@@ -7,8 +7,14 @@
// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.DependencyInjection;
+/// DI registration extensions for FluentlyHttpClient.Entity.
public static class ServiceCollectionExtensions
{
+ ///
+ /// Register FluentlyHttpClient Entity services (SQL Server-backed response cache) using the given connection string.
+ ///
+ /// The service collection.
+ /// SQL Server connection string.
public static IServiceCollection AddFluentlyHttpClientEntity(this IServiceCollection services, string connectionString)
{
if (string.IsNullOrWhiteSpace(connectionString))
@@ -24,6 +30,12 @@ public static IServiceCollection AddFluentlyHttpClientEntity(this IServiceCollec
return services.AddFluentlyHttpClientEntity(conn, builder => builder.EnableRetryOnFailure());
}
+ ///
+ /// Register FluentlyHttpClient Entity services using a pre-built .
+ ///
+ /// The service collection.
+ /// Pre-configured connection string builder.
+ /// Optional SQL Server options builder (e.g. retry policy).
public static IServiceCollection AddFluentlyHttpClientEntity(
this IServiceCollection services,
SqlConnectionStringBuilder connectionStringBuilder,
diff --git a/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs b/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs
index 70487ed..ead3678 100644
--- a/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs
+++ b/src/FluentlyHttpClient/Caching/HttpResponseSerializer.cs
@@ -4,12 +4,20 @@
namespace FluentlyHttpClient.Caching;
+/// Contract for serializing and deserializing objects to and from a store.
public interface IHttpResponseSerializer
{
+ /// Serialize a to a store object.
+ /// Type of store to serialize into.
+ /// The HTTP response to serialize.
Task Serialize(FluentHttpResponse response) where THttpResponseStore : IHttpResponseStore, new();
+
+ /// Deserialize a store object back to a .
+ /// The store object to deserialize.
Task Deserialize(IHttpResponseStore item);
}
+/// Default implementation of .
public class HttpResponseSerializer : IHttpResponseSerializer
{
///
diff --git a/src/FluentlyHttpClient/Caching/HttpResponseStore.cs b/src/FluentlyHttpClient/Caching/HttpResponseStore.cs
index 95f70ce..00cca66 100644
--- a/src/FluentlyHttpClient/Caching/HttpResponseStore.cs
+++ b/src/FluentlyHttpClient/Caching/HttpResponseStore.cs
@@ -1,29 +1,55 @@
namespace FluentlyHttpClient.Caching;
+///
+/// Contract for storing a serialized HTTP response.
+///
public interface IHttpResponseStore
{
+ /// Gets or sets the client identifier name.
string? Name { get; set; }
+ /// Gets or sets the request hash key.
string? Hash { get; set; }
+ /// Gets or sets the request URL.
string? Url { get; set; }
+ /// Gets or sets the response body content as a string.
string? Content { get; set; }
+ /// Gets or sets the response headers.
FluentHttpHeaders? Headers { get; set; }
+ /// Gets or sets the HTTP status code.
int StatusCode { get; set; }
+ /// Gets or sets the reason phrase.
string? ReasonPhrase { get; set; }
+ /// Gets or sets the HTTP version string.
string? Version { get; set; }
+ /// Gets or sets the content headers.
FluentHttpHeaders? ContentHeaders { get; set; }
+ /// Gets or sets the original request message.
HttpRequestMessage? RequestMessage { get; set; }
}
+///
+/// Default in-memory store for a serialized HTTP response.
+///
public class HttpResponseStore : IHttpResponseStore
{
+ ///
public string? Name { get; set; }
+ ///
public string? Hash { get; set; }
+ ///
public string? Url { get; set; }
+ ///
public string? Content { get; set; }
+ ///
public FluentHttpHeaders? Headers { get; set; }
+ ///
public int StatusCode { get; set; }
+ ///
public string? ReasonPhrase { get; set; }
+ ///
public string? Version { get; set; }
+ ///
public FluentHttpHeaders? ContentHeaders { get; set; }
+ ///
public HttpRequestMessage? RequestMessage { get; set; }
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/Caching/ResponseCacheMiddleware.cs b/src/FluentlyHttpClient/Caching/ResponseCacheMiddleware.cs
index eaaf327..17d99d7 100644
--- a/src/FluentlyHttpClient/Caching/ResponseCacheMiddleware.cs
+++ b/src/FluentlyHttpClient/Caching/ResponseCacheMiddleware.cs
@@ -14,6 +14,7 @@ public class ResponseCacheHttpMiddlewareOptions
/// Ignore the request from being cached.
///
public bool ShouldIgnore { get; set; }
+ /// Predicate to match which requests should be cached. When all requests are cached.
public Predicate? Matcher { get; set; }
///
@@ -54,7 +55,7 @@ public async Task Invoke(FluentHttpMiddlewareContext context
{
var request = context.Request;
- var options = request.GetResponseCachingOptions(_options);
+ var options = request.GetResponseCachingOptions(_options) ?? _options;
if (options.ShouldIgnore || options.Matcher != null && !options.Matcher(request))
return await _next(context);
diff --git a/src/FluentlyHttpClient/Caching/ResponseCacheService.cs b/src/FluentlyHttpClient/Caching/ResponseCacheService.cs
index 1b79ddc..1ccb2ec 100644
--- a/src/FluentlyHttpClient/Caching/ResponseCacheService.cs
+++ b/src/FluentlyHttpClient/Caching/ResponseCacheService.cs
@@ -2,27 +2,36 @@
namespace FluentlyHttpClient.Caching;
+/// Contract for caching HTTP responses by request hash.
public interface IResponseCacheService
{
+ /// Get a cached response by hash, or if not found.
Task Get(string hash);
+
+ /// Store a response under the given hash.
Task Set(string hash, FluentHttpResponse response);
}
+/// In-memory implementation of backed by .
public class MemoryResponseCacheService : IResponseCacheService
{
private readonly IMemoryCache _cache;
+ /// Initializes a new instance of .
+ /// The memory cache to use.
public MemoryResponseCacheService(IMemoryCache cache)
{
_cache = cache;
}
- public Task Get(string hash)
+ ///
+ public async Task Get(string hash)
{
var result = _cache.Get(hash);
- return result?.Clone() ?? Task.FromResult(null);
+ return result != null ? await result.Clone() : null;
}
+ ///
public Task Set(string hash, FluentHttpResponse response)
{
_cache.Set(hash, response);
diff --git a/src/FluentlyHttpClient/Exceptions.cs b/src/FluentlyHttpClient/Exceptions.cs
index ee9589f..bce510a 100644
--- a/src/FluentlyHttpClient/Exceptions.cs
+++ b/src/FluentlyHttpClient/Exceptions.cs
@@ -5,15 +5,13 @@ namespace FluentlyHttpClient;
///
public class RequestValidationException : Exception
{
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+ /// Initializes a new instance with the specified message.
public RequestValidationException(string message) : base(message)
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
}
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+ /// Initializes a new instance with the specified message and inner exception.
public RequestValidationException(string message, Exception inner) : base(message, inner)
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
}
@@ -38,15 +36,13 @@ public static RequestValidationException FieldNotSpecified(string field)
///
public class ClientBuilderValidationException : Exception
{
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+ /// Initializes a new instance with the specified message.
public ClientBuilderValidationException(string message) : base(message)
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
}
-#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
+ /// Initializes a new instance with the specified message and inner exception.
public ClientBuilderValidationException(string message, Exception inner) : base(message, inner)
-#pragma warning restore CS1591 // Missing XML comment for publicly visible type or member
{
}
diff --git a/src/FluentlyHttpClient/FluentHttpClient.cs b/src/FluentlyHttpClient/FluentHttpClient.cs
index 713fc0d..fa23b59 100644
--- a/src/FluentlyHttpClient/FluentHttpClient.cs
+++ b/src/FluentlyHttpClient/FluentHttpClient.cs
@@ -15,7 +15,7 @@ public interface IFluentHttpClient : IDisposable
///
/// Gets the base uri address for each request.
///
- string BaseUrl { get; }
+ string? BaseUrl { get; }
///
/// Underlying HTTP client. This should be avoided from being used,
@@ -93,7 +93,7 @@ public class FluentHttpClient : IFluentHttpClient
public string Identifier { get; }
///
- public string BaseUrl { get; }
+ public string? BaseUrl { get; }
///
public HttpClient RawHttpClient { get; }
@@ -191,6 +191,7 @@ public async Task Send(FluentHttpRequest request)
return executionContext.Response;
}
+ ///
public async Task Send(HttpRequestMessage request)
{
ArgumentNullException.ThrowIfNull(request, nameof(request));
@@ -237,6 +238,8 @@ private HttpClient Configure(FluentHttpClientOptions options)
public void Dispose()
=> RawHttpClient?.Dispose();
+ /// Implicitly converts a to the underlying .
+ /// The fluent client to convert.
public static implicit operator HttpClient(FluentHttpClient client)
=> client.RawHttpClient;
diff --git a/src/FluentlyHttpClient/FluentHttpClientBuilder.cs b/src/FluentlyHttpClient/FluentHttpClientBuilder.cs
index afa7979..18298f0 100644
--- a/src/FluentlyHttpClient/FluentHttpClientBuilder.cs
+++ b/src/FluentlyHttpClient/FluentHttpClientBuilder.cs
@@ -85,36 +85,50 @@ public FluentHttpClientBuilder WithTimeout(TimeSpan timeout)
return this;
}
+ /// Set a single HTTP header for all requests built by this client.
+ /// Header name.
+ /// Header value.
public FluentHttpClientBuilder WithHeader(string key, string value)
{
_headers.Set(key, value);
return this;
}
+ /// Set a single HTTP header with multiple values for all requests built by this client.
+ /// Header name.
+ /// Header values.
public FluentHttpClientBuilder WithHeader(string key, StringValues values)
{
_headers.Set(key, values);
return this;
}
+ /// Set multiple HTTP headers from a string dictionary for all requests built by this client.
+ /// Headers to set.
public FluentHttpClientBuilder WithHeaders(IDictionary headers)
{
_headers.SetRange(headers);
return this;
}
+ /// Set multiple HTTP headers from a string-array dictionary for all requests built by this client.
+ /// Headers to set.
public FluentHttpClientBuilder WithHeaders(IDictionary headers)
{
_headers.SetRange(headers);
return this;
}
+ /// Set multiple HTTP headers from a dictionary for all requests built by this client.
+ /// Headers to set.
public FluentHttpClientBuilder WithHeaders(IDictionary headers)
{
_headers.SetRange(headers);
return this;
}
+ /// Set multiple HTTP headers from a instance for all requests built by this client.
+ /// Headers to set.
public FluentHttpClientBuilder WithHeaders(FluentHttpHeaders headers)
{
_headers.SetRange(headers);
diff --git a/src/FluentlyHttpClient/FluentHttpClientFactory.cs b/src/FluentlyHttpClient/FluentHttpClientFactory.cs
index 02deb19..1e74e6a 100644
--- a/src/FluentlyHttpClient/FluentHttpClientFactory.cs
+++ b/src/FluentlyHttpClient/FluentHttpClientFactory.cs
@@ -1,5 +1,9 @@
namespace FluentlyHttpClient;
+///
+/// Options for allowing global client defaults to be configured.
+///
+/// Optional action to configure defaults for every .
public record FluentHttpClientFactoryOptions(
Action? ConfigureDefaults
);
@@ -73,6 +77,7 @@ public class FluentHttpClientFactory : IFluentHttpClientFactory
private readonly Dictionary _clientsMap = [];
private Action? _configure;
+ /// Gets the number of registered HTTP clients.
public int Count => _clientsMap.Count;
///
diff --git a/src/FluentlyHttpClient/FluentHttpHeaders.cs b/src/FluentlyHttpClient/FluentHttpHeaders.cs
index f00eb6e..a3cc467 100644
--- a/src/FluentlyHttpClient/FluentHttpHeaders.cs
+++ b/src/FluentlyHttpClient/FluentHttpHeaders.cs
@@ -39,13 +39,14 @@ public FluentHttpHeadersOptions WithHashingExclude(Predicate
/// Collection of headers and their values.
///
-public partial class FluentHttpHeaders : IFluentHttpHeaderBuilder, IEnumerable>
+public partial class FluentHttpHeaders : IFluentHttpHeaderBuilder, IEnumerable>
{
private static readonly FluentHttpHeadersOptions DefaultOptions = new();
private FluentHttpHeadersOptions _options = DefaultOptions;
- private readonly Dictionary _data = new(StringComparer.OrdinalIgnoreCase);
+ private readonly Dictionary _data = new(StringComparer.OrdinalIgnoreCase);
- public string[]? this[string key]
+ /// Gets or sets the header values for the specified key.
+ public string?[]? this[string key]
{
get => _data[key];
set => _data[key] = value;
@@ -200,7 +201,7 @@ public FluentHttpHeaders AddRange(IDictionary headers)
public FluentHttpHeaders AddRange(IDictionary headers)
{
foreach (var header in headers)
- Add(header.Key, header.Value.ToArray());
+ Add(header.Key, header.Value);
return this;
}
@@ -231,7 +232,7 @@ public StringValues Get(string header)
///
/// Header to try get.
public string? GetValue(string header)
- => _data.TryGetValue(header, out var value) ? value[0] : null;
+ => _data.TryGetValue(header, out var value) ? value?[0] : null;
///
/// Set single header add/update if exists instead of throwing.
@@ -362,7 +363,8 @@ public string ToHashString()
return headers.ToFormattedString();
}
- public IEnumerator> GetEnumerator() => _data.GetEnumerator();
+ ///
+ public IEnumerator> GetEnumerator() => _data.GetEnumerator();
///
/// Converts to string.
@@ -374,7 +376,7 @@ 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);
@@ -411,7 +413,7 @@ public StringValues AcceptLanguage
public string? Authorization
{
get => Get(HeaderTypes.Authorization);
- set => this[HeaderTypes.Authorization] = [value];
+ set => this[HeaderTypes.Authorization] = value is null ? null : [value];
}
///
@@ -420,7 +422,7 @@ public string? Authorization
public string? CacheControl
{
get => Get(HeaderTypes.CacheControl);
- set => this[HeaderTypes.CacheControl] = [value];
+ set => this[HeaderTypes.CacheControl] = value is null ? null : [value];
}
///
@@ -429,7 +431,7 @@ public string? CacheControl
public string? ContentType
{
get => Get(HeaderTypes.ContentType);
- set => this[HeaderTypes.ContentType] = [value];
+ set => this[HeaderTypes.ContentType] = value is null ? null : [value];
}
///
@@ -438,7 +440,7 @@ public string? ContentType
public string? UserAgent
{
get => Get(HeaderTypes.UserAgent);
- set => this[HeaderTypes.UserAgent] = [value];
+ set => this[HeaderTypes.UserAgent] = value is null ? null : [value];
}
///
@@ -456,6 +458,6 @@ public StringValues? XForwardedFor
public string? XForwardedHost
{
get => Get(HeaderTypes.XForwardedHost);
- set => this[HeaderTypes.XForwardedHost] = [value];
+ set => this[HeaderTypes.XForwardedHost] = value is null ? null : [value];
}
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/FluentHttpRequestBuilder.cs b/src/FluentlyHttpClient/FluentHttpRequestBuilder.cs
index b452cfd..df1b1dc 100644
--- a/src/FluentlyHttpClient/FluentHttpRequestBuilder.cs
+++ b/src/FluentlyHttpClient/FluentHttpRequestBuilder.cs
@@ -57,7 +57,7 @@ public FluentHttpHeaders Headers
///
/// Gets the base url from the HTTP client.
///
- public string BaseUrl => _fluentHttpClient.BaseUrl;
+ public string? BaseUrl => _fluentHttpClient.BaseUrl;
///
///
@@ -112,36 +112,50 @@ public FluentHttpRequestBuilder WithVersionPolicy(HttpVersionPolicy policy)
return this;
}
+ /// Set a single HTTP header for this request.
+ /// Header name.
+ /// Header value.
public FluentHttpRequestBuilder WithHeader(string key, string value)
{
Headers.Set(key, value);
return this;
}
+ /// Set a single HTTP header with multiple values for this request.
+ /// Header name.
+ /// Header values.
public FluentHttpRequestBuilder WithHeader(string key, StringValues values)
{
Headers.Set(key, values);
return this;
}
+ /// Set multiple HTTP headers from a string dictionary for this request.
+ /// Headers to set.
public FluentHttpRequestBuilder WithHeaders(IDictionary headers)
{
Headers.SetRange(headers);
return this;
}
+ /// Set multiple HTTP headers from a string-array dictionary for this request.
+ /// Headers to set.
public FluentHttpRequestBuilder WithHeaders(IDictionary headers)
{
Headers.SetRange(headers);
return this;
}
+ /// Set multiple HTTP headers from a dictionary for this request.
+ /// Headers to set.
public FluentHttpRequestBuilder WithHeaders(IDictionary headers)
{
Headers.SetRange(headers);
return this;
}
+ /// Set multiple HTTP headers from a instance for this request.
+ /// Headers to set.
public FluentHttpRequestBuilder WithHeaders(FluentHttpHeaders headers)
{
Headers.SetRange(headers);
@@ -349,6 +363,8 @@ public FluentHttpRequest Build()
{
foreach (var header in _headers)
{
+ if (header.Value is null)
+ continue;
if (header.Key == HeaderTypes.UserAgent)
httpRequest.Headers.TryAddWithoutValidation(header.Key, header.Value);
else
@@ -385,7 +401,7 @@ private static string BuildQueryString(object? queryParams, QueryStringOptions?
if (dict.Count == 0)
return string.Empty;
- var queryCollection = new Dictionary();
+ var queryCollection = new Dictionary();
foreach (var item in dict)
queryCollection[item.Key] = item.Value;
diff --git a/src/FluentlyHttpClient/FluentHttpResponse.cs b/src/FluentlyHttpClient/FluentHttpResponse.cs
index 4bfcd98..27b0b78 100644
--- a/src/FluentlyHttpClient/FluentHttpResponse.cs
+++ b/src/FluentlyHttpClient/FluentHttpResponse.cs
@@ -37,7 +37,7 @@ public class FluentHttpResponse : IFluentHttpMessageState
///
/// Gets readable string for debugger.
///
- protected string DebuggerDisplay => $"[{(int)StatusCode}] '{ReasonPhrase}', Request: {{ [{Message.RequestMessage.Method}] '{Message.RequestMessage.RequestUri}' }}";
+ protected string DebuggerDisplay => $"[{(int)StatusCode}] '{ReasonPhrase}', Request: {{ [{Message.RequestMessage?.Method}] '{Message.RequestMessage?.RequestUri}' }}";
///
/// Gets the underlying HTTP response message.
diff --git a/src/FluentlyHttpClient/GraphQL/GqlRequest.cs b/src/FluentlyHttpClient/GraphQL/GqlRequest.cs
index a7b74f3..0f77913 100644
--- a/src/FluentlyHttpClient/GraphQL/GqlRequest.cs
+++ b/src/FluentlyHttpClient/GraphQL/GqlRequest.cs
@@ -13,15 +13,10 @@ public class GqlRequest
///
/// Gets or sets GraphQL query.
///
- public string Query { get; set; }
+ public required string Query { get; set; }
///
/// Gets or sets GraphQL query variables.
///
public object? Variables { get; set; }
-}
-
-[Obsolete("Use 'GqlRequest' instead.")]
-public class GqlQuery : GqlRequest
-{
}
\ No newline at end of file
diff --git a/src/FluentlyHttpClient/HttpMessageExtensions.cs b/src/FluentlyHttpClient/HttpMessageExtensions.cs
index 3458656..81e26c3 100644
--- a/src/FluentlyHttpClient/HttpMessageExtensions.cs
+++ b/src/FluentlyHttpClient/HttpMessageExtensions.cs
@@ -3,6 +3,7 @@ namespace FluentlyHttpClient;
internal static class HttpMessageExtensions
{
private const string RequestIdProperty = "request-id";
+ private static readonly HttpRequestOptionsKey RequestIdKey = new(RequestIdProperty);
internal static FluentHttpRequest ToFluentHttpRequest(
this HttpRequestMessage request,
@@ -12,20 +13,25 @@ FluentHttpClient client
var builder = client
.CreateRequest()
.WithMethod(request.Method)
- .WithUri(request.RequestUri.ToString())
- .WithBodyContent(request.Content)
+ .WithUri(request.RequestUri?.ToString() ?? string.Empty)
;
- foreach (var prop in request.Properties)
- builder.WithItem(prop.Key, prop.Value);
+ if (request.Content is not null)
+ builder.WithBodyContent(request.Content);
+
+ foreach (var prop in request.Options)
+ {
+ if (prop.Value is not null)
+ builder.WithItem(prop.Key, prop.Value);
+ }
return new(builder, request);
}
internal static string? GetRequestId(this HttpRequestMessage request)
{
- if (request.Properties.TryGetValue(RequestIdProperty, out var requestKey))
- return (string)requestKey;
+ if (request.Options.TryGetValue(RequestIdKey, out var requestKey))
+ return requestKey;
return null;
}
@@ -33,7 +39,7 @@ FluentHttpClient client
internal static string AddRequestId(this HttpRequestMessage request, string? id = null)
{
var requestId = id ?? Guid.NewGuid().ToString();
- request.Properties.Add(RequestIdProperty, requestId);
+ request.Options.Set(RequestIdKey, requestId);
return requestId;
}
diff --git a/src/FluentlyHttpClient/MediaFormatters/SystemTextJsonMediaTypeFormatter.cs b/src/FluentlyHttpClient/MediaFormatters/SystemTextJsonMediaTypeFormatter.cs
index 0feaede..04d9338 100644
--- a/src/FluentlyHttpClient/MediaFormatters/SystemTextJsonMediaTypeFormatter.cs
+++ b/src/FluentlyHttpClient/MediaFormatters/SystemTextJsonMediaTypeFormatter.cs
@@ -4,6 +4,7 @@
namespace FluentlyHttpClient.MediaFormatters;
// todo: move to separate lib?
+/// Media type formatter that uses System.Text.Json for JSON serialization.
public class SystemTextJsonMediaTypeFormatter : MediaTypeFormatter
{
private const string MediaType = "application/json";
@@ -30,12 +31,14 @@ public SystemTextJsonMediaTypeFormatter(JsonSerializerOptions? options)
};
}
+ ///
public override bool CanReadType(Type type)
{
ArgumentNullException.ThrowIfNull(type, nameof(type));
return IsAllowedType(type);
}
+ ///
public override bool CanWriteType(Type type)
{
ArgumentNullException.ThrowIfNull(type, nameof(type));
@@ -53,14 +56,17 @@ private static bool IsAllowedType(Type t)
return false;
}
+ ///
public override async Task