Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use custom JsonSerializerOptions on public contracts serialization #107

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 21 additions & 7 deletions src/Convey.WebApi.CQRS/src/Convey.WebApi.CQRS/Extensions.cs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
using System;
using System.Threading.Tasks;
using Convey.CQRS.Commands;
using Convey.CQRS.Queries;
using Convey.WebApi.CQRS.Builders;
using Convey.WebApi.CQRS.Middlewares;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Threading.Tasks;

namespace Convey.WebApi.CQRS;

Expand Down Expand Up @@ -48,11 +50,23 @@ public static IApplicationBuilder UsePublicContracts(this IApplicationBuilder ap
bool attributeRequired, string endpoint = "/_contracts")
=> app.UsePublicContracts(endpoint, null, attributeRequired);

public static IApplicationBuilder UsePublicContracts(this IApplicationBuilder app,
string endpoint = "/_contracts", Type attributeType = null, bool attributeRequired = true)
=> app.UseMiddleware<PublicContractsMiddleware>(string.IsNullOrWhiteSpace(endpoint) ? "/_contracts" :
endpoint.StartsWith("/") ? endpoint : $"/{endpoint}", attributeType ?? typeof(PublicContractAttribute),
attributeRequired);
public static IApplicationBuilder UsePublicContracts(
this IApplicationBuilder app,
string endpoint = "/_contracts",
Type attributeType = null,
bool attributeRequired = true,
JsonSerializerOptions jsonSerializerOptions = null)
=> app.UseMiddleware<PublicContractsMiddleware>(
string.IsNullOrWhiteSpace(endpoint) ? "/_contracts" : endpoint.StartsWith("/") ? endpoint : $"/{endpoint}",
attributeType ?? typeof(PublicContractAttribute),
attributeRequired,
jsonSerializerOptions ?? new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
WriteIndented = true
});

public static Task SendAsync<T>(this HttpContext context, T command) where T : class, ICommand
=> context.RequestServices.GetRequiredService<ICommandDispatcher>().SendAsync(command);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,34 +16,38 @@ namespace Convey.WebApi.CQRS.Middlewares;
public class PublicContractsMiddleware
{
private const string ContentType = "application/json";
private readonly RequestDelegate _next;
private readonly string _endpoint;
private readonly bool _attributeRequired;

private static readonly JsonSerializerOptions SerializerOptions = new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
WriteIndented = true
};

private static readonly ContractTypes Contracts = new();
private static int _initialized;
private static string _serializedContracts = "{}";

public PublicContractsMiddleware(RequestDelegate next, string endpoint, Type attributeType,
bool attributeRequired)
private readonly RequestDelegate _next;
private readonly string _endpoint;

public PublicContractsMiddleware(
RequestDelegate next,
string endpoint,
Type attributeType,
bool attributeRequired,
JsonSerializerOptions jsonSerializerOptions)
{
_next = next;
_endpoint = endpoint;
_attributeRequired = attributeRequired;

if (_initialized == 1)
{
return;
}

Load(attributeType);
jsonSerializerOptions ??= new()
{
PropertyNameCaseInsensitive = true,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
Converters = { new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) },
WriteIndented = true
};

Load(attributeType, attributeRequired, jsonSerializerOptions);
}

public Task InvokeAsync(HttpContext context)
Expand All @@ -59,19 +63,27 @@ public Task InvokeAsync(HttpContext context)
return Task.CompletedTask;
}

private void Load(Type attributeType)
private void Load(
Type attributeType,
bool attributeRequired,
JsonSerializerOptions jsonSerializerOptions)
{
if (Interlocked.Exchange(ref _initialized, 1) == 1)
{
return;
}

var assemblies = AppDomain.CurrentDomain.GetAssemblies();
var contracts = assemblies.SelectMany(a => a.GetTypes())
.Where(t => (!_attributeRequired || t.GetCustomAttribute(attributeType) is not null) && !t.IsInterface)
.ToArray();

foreach (var command in contracts.Where(t => typeof(ICommand).IsAssignableFrom(t)))
var contractTypes =
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t =>
!t.IsInterface &&
(!attributeRequired || t.GetCustomAttribute(attributeType) is not null) &&
(typeof(ICommand).IsAssignableFrom(t) ||
typeof(IEvent).IsAssignableFrom(t)))
.ToArray();

foreach (var command in contractTypes.Where(t => typeof(ICommand).IsAssignableFrom(t)))
{
var instance = command.GetDefaultInstance();
var name = instance.GetType().Name;
Expand All @@ -84,8 +96,7 @@ private void Load(Type attributeType)
Contracts.Commands[name] = instance;
}

foreach (var @event in contracts.Where(t => typeof(IEvent).IsAssignableFrom(t) &&
t != typeof(RejectedEvent)))
foreach (var @event in contractTypes.Where(t => typeof(IEvent).IsAssignableFrom(t) && t != typeof(RejectedEvent)))
{
var instance = @event.GetDefaultInstance();
var name = instance.GetType().Name;
Expand All @@ -98,7 +109,7 @@ private void Load(Type attributeType)
Contracts.Events[name] = instance;
}

_serializedContracts = JsonSerializer.Serialize(Contracts, SerializerOptions);
_serializedContracts = JsonSerializer.Serialize(Contracts, jsonSerializerOptions);
}

private class ContractTypes
Expand Down