diff --git a/src/TodoApp/ApiEndpoints.cs b/src/TodoApp/ApiEndpoints.cs index 0101f2bf..e10391b6 100644 --- a/src/TodoApp/ApiEndpoints.cs +++ b/src/TodoApp/ApiEndpoints.cs @@ -1,6 +1,7 @@ // Copyright (c) Martin Costello, 2024. All rights reserved. // Licensed under the Apache 2.0 license. See the LICENSE file in the project root for full license information. +using System.Text.Json.Serialization; using Microsoft.AspNetCore.Http.HttpResults; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; @@ -33,6 +34,15 @@ public static IServiceCollection AddTodoApi(this IServiceCollection services) services.AddScoped(); services.AddScoped(); + // Use the same JSON serializer options for OpenAPI as for the API itself + services.ConfigureHttpJsonOptions(options => + { + options.SerializerOptions.DefaultIgnoreCondition = TodoJsonSerializerContext.Default.Options.DefaultIgnoreCondition; + options.SerializerOptions.NumberHandling = TodoJsonSerializerContext.Default.Options.NumberHandling; + options.SerializerOptions.PropertyNamingPolicy = TodoJsonSerializerContext.Default.Options.PropertyNamingPolicy; + options.SerializerOptions.WriteIndented = TodoJsonSerializerContext.Default.Options.WriteIndented; + }); + // Configure an EFCore data context to store the Todos backed by SQLite services.AddDbContext((serviceProvider, options) => { diff --git a/src/TodoApp/OpenApi/ExamplesProcessor.cs b/src/TodoApp/OpenApi/ExamplesProcessor.cs index 351f40cb..080103d8 100644 --- a/src/TodoApp/OpenApi/ExamplesProcessor.cs +++ b/src/TodoApp/OpenApi/ExamplesProcessor.cs @@ -34,7 +34,7 @@ protected void Process(OpenApiOperation operation, ApiDescription description) TryAddRequestExamples(body, description, examples); } - TryAddResponseExamples(operation.Responses, description, examples); + TryAddResponseExamples(operation.Responses ?? [], description, examples); } protected void Process(OpenApiSchema schema, Type type) @@ -81,8 +81,7 @@ private static void TryAddParameterExamples( if (metadata?.GenerateExample(Context) is { } value) { // Find the parameter that corresponds to the argument and set its example - var parameter = parameters.FirstOrDefault((p) => p.Name == argument.Name); - if (parameter is not null) + if (parameters.FirstOrDefault((p) => p.Name == argument.Name) is OpenApiParameter parameter) { parameter.Example ??= value; } diff --git a/src/TodoApp/OpenApi/Swashbuckle/AddDocumentTagsFilter.cs b/src/TodoApp/OpenApi/Swashbuckle/AddDocumentTagsFilter.cs index 7674ab72..837bd683 100644 --- a/src/TodoApp/OpenApi/Swashbuckle/AddDocumentTagsFilter.cs +++ b/src/TodoApp/OpenApi/Swashbuckle/AddDocumentTagsFilter.cs @@ -13,6 +13,7 @@ public class AddDocumentTagsFilter : IDocumentFilter { public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context) { + swaggerDoc.Tags ??= []; swaggerDoc.Tags.Add(new() { Name = "TodoApp" }); } } diff --git a/src/TodoApp/TodoJsonSerializerContext.cs b/src/TodoApp/TodoJsonSerializerContext.cs index 528b94b9..4fd257de 100644 --- a/src/TodoApp/TodoJsonSerializerContext.cs +++ b/src/TodoApp/TodoJsonSerializerContext.cs @@ -20,6 +20,7 @@ namespace TodoApp; [JsonSerializable(typeof(TodoListViewModel))] [JsonSourceGenerationOptions( DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull, + NumberHandling = JsonNumberHandling.Strict, PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase, WriteIndented = true)] public sealed partial class TodoJsonSerializerContext : JsonSerializerContext;