using System.Globalization; using System.Text.Json; using System.Text.Json.Serialization; using Elsa.Workflows; using FastEndpoints; using JetBrains.Annotations; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; // ReSharper disable once CheckNamespace namespace Elsa.Extensions; /// /// Provides extension methods to add the FastEndpoints middleware configured for use with Elsa API endpoints. /// [PublicAPI] public static class WebApplicationExtensions { /// /// Register the FastEndpoints middleware configured for use with with Elsa API endpoints. /// /// /// The route prefix to apply to Elsa API endpoints. /// E.g. "elsa/api" will expose endpoints like this: "/elsa/api/workflow-definitions" public static IApplicationBuilder UseWorkflowsApi(this IApplicationBuilder app, string routePrefix = "elsa/api") { return app.UseFastEndpoints(config => { config.Endpoints.RoutePrefix = routePrefix; config.Serializer.RequestDeserializer = DeserializeRequestAsync; config.Serializer.ResponseSerializer = SerializeRequestAsync; config.Binding.ValueParserFor(s => new(DateTimeOffset.TryParse(s.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result), result)); }); } /// /// Register the FastEndpoints middleware configured for use with with Elsa API endpoints. /// /// The to register the endpoints with. /// The route prefix to apply to Elsa API endpoints. /// E.g. "elsa/api" will expose endpoints like this: "/elsa/api/workflow-definitions" public static IEndpointRouteBuilder MapWorkflowsApi(this IEndpointRouteBuilder routes, string routePrefix = "elsa/api") => routes.MapFastEndpoints(config => { config.Endpoints.RoutePrefix = routePrefix; config.Serializer.RequestDeserializer = DeserializeRequestAsync; config.Serializer.ResponseSerializer = SerializeRequestAsync; }); private static ValueTask DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) { var serializer = httpRequest.HttpContext.RequestServices.GetRequiredService(); var options = serializer.GetOptions(); return serializerContext == null ? JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, options, cancellationToken) : JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, serializerContext, cancellationToken); } private static Task SerializeRequestAsync(HttpResponse httpResponse, object? dto, string contentType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) { var serializer = httpResponse.HttpContext.RequestServices.GetRequiredService(); var options = serializer.GetOptions(); httpResponse.ContentType = contentType; return serializerContext == null ? JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), options, cancellationToken) : JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), serializerContext, cancellationToken); } }