From aee729a61ec72de5188a410519dee7bad994eecb Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Fri, 21 Feb 2025 14:04:28 +0100 Subject: [PATCH 1/7] Improve existing test --- .../OpenApiDocumentServiceTests.Responses.cs | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs index 61ef643260b6..9cbd2e2ae895 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs @@ -315,11 +315,11 @@ public async Task GetOpenApiResponse_UsesDescriptionSetByUser() const string expectedBadRequestDescription = "Validation failed for the request"; // Act - builder.MapGet("/api/todos", - [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = expectedCreatedDescription)] - [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)] + builder.MapPost("/api/todos", + [ProducesResponseType(StatusCodes.Status200OK, Description = expectedCreatedDescription)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL () => - { }); + { return TypedResults.Ok(new Todo(1, "Lorem", true, DateTime.UtcNow)); }); // This code doesn't return Bad Request, but that doesn't matter for this test. // Assert await VerifyOpenApiDocument(builder, document => @@ -328,7 +328,7 @@ await VerifyOpenApiDocument(builder, document => Assert.Collection(operation.Responses.OrderBy(r => r.Key), response => { - Assert.Equal("201", response.Key); + Assert.Equal("200", response.Key); Assert.Equal(expectedCreatedDescription, response.Value.Description); }, response => @@ -346,11 +346,11 @@ public async Task GetOpenApiResponse_UsesStatusCodeReasonPhraseWhenExplicitDescr var builder = CreateBuilder(); // Act - builder.MapGet("/api/todos", - [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = null)] // Explicitly set to NULL + builder.MapPost("/api/todos", + [ProducesResponseType(StatusCodes.Status200OK, Description = null)] // Explicitly set to NULL [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL () => - { }); + { return TypedResults.Ok(new Todo(1, "Lorem", true, DateTime.UtcNow)); }); // This code doesn't return Bad Request, but that doesn't matter for this test. // Assert await VerifyOpenApiDocument(builder, document => @@ -359,8 +359,8 @@ await VerifyOpenApiDocument(builder, document => Assert.Collection(operation.Responses.OrderBy(r => r.Key), response => { - Assert.Equal("201", response.Key); - Assert.Equal("Created", response.Value.Description); + Assert.Equal("200", response.Key); + Assert.Equal("OK", response.Value.Description); }, response => { From ef31d31054d90b38ecfe51787a7d116d727ca3f0 Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Fri, 21 Feb 2025 14:09:24 +0100 Subject: [PATCH 2/7] Add more tests for regression --- .../OpenApiDocumentServiceTests.Responses.cs | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs index 9cbd2e2ae895..77316c3b18b5 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs @@ -305,6 +305,43 @@ await VerifyOpenApiDocument(builder, document => }); } + /// + /// Regression test for https://github.com/dotnet/aspnetcore/issues/60518 + /// + [Fact] + public async Task GetOpenApiResponse_WithEmptyMethodBody_UsesDescriptionSetByUser() + { + // Arrange + var builder = CreateBuilder(); + + const string expectedCreatedDescription = "A new todo item was created"; + const string expectedBadRequestDescription = "Validation failed for the request"; + + // Act + builder.MapPost("/api/todos", + [ProducesResponseType(StatusCodes.Status200OK, Description = expectedCreatedDescription)] + [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL + () => + { }); + + // Assert + await VerifyOpenApiDocument(builder, document => + { + var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values); + Assert.Collection(operation.Responses.OrderBy(r => r.Key), + response => + { + Assert.Equal("200", response.Key); + Assert.Equal(expectedCreatedDescription, response.Value.Description); + }, + response => + { + Assert.Equal("400", response.Key); + Assert.Equal(expectedBadRequestDescription, response.Value.Description); + }); + }); + } + [Fact] public async Task GetOpenApiResponse_UsesDescriptionSetByUser() { @@ -369,4 +406,38 @@ await VerifyOpenApiDocument(builder, document => }); }); } + + /// + /// Regression test for https://github.com/dotnet/aspnetcore/issues/60518 + /// + [Fact] + public async Task GetOpenApiResponse_WithEmptyMethodBody_UsesStatusCodeReasonPhraseWhenExplicitDescriptionIsMissing() + { + // Arrange + var builder = CreateBuilder(); + + // Act + builder.MapPost("/api/todos", + [ProducesResponseType(StatusCodes.Status200OK, Description = null)] // Explicitly set to NULL + [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL + () => + { }); + + // Assert + await VerifyOpenApiDocument(builder, document => + { + var operation = Assert.Single(document.Paths["/api/todos"].Operations.Values); + Assert.Collection(operation.Responses.OrderBy(r => r.Key), + response => + { + Assert.Equal("200", response.Key); + Assert.Equal("OK", response.Value.Description); + }, + response => + { + Assert.Equal("400", response.Key); + Assert.Equal("Bad Request", response.Value.Description); + }); + }); + } } From 4730e8fe5189cbcdd66290dd4d8f577b046a8f0c Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Fri, 21 Feb 2025 15:03:16 +0100 Subject: [PATCH 3/7] Add tests which now succeed --- .../EndpointMetadataApiDescriptionProvider.cs | 3 + ...pointMetadataApiDescriptionProviderTest.cs | 75 +++++++++++++++++++ .../OpenApiDocumentServiceTests.Responses.cs | 4 +- 3 files changed, 80 insertions(+), 2 deletions(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 175c35fb0567..819bae15e226 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -375,6 +375,9 @@ private static void AddSupportedResponseTypes( apiResponseType.ApiResponseFormats.Add(defaultResponseFormat); } + // We set the Description to the first non-null value we find that matches the status code. + apiResponseType.Description ??= responseMetadataTypes.FirstOrDefault(x => x.StatusCode == apiResponseType.StatusCode && x.Description is not null)?.Description; + if (!supportedResponseTypes.Any(existingResponseType => existingResponseType.StatusCode == apiResponseType.StatusCode)) { supportedResponseTypes.Add(apiResponseType); diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 337865ec5f79..251200c39421 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -300,6 +300,81 @@ public void AddsMultipleResponseFormatsForTypedResults() Assert.Empty(badRequestResponseType.ApiResponseFormats); } + [Fact] + public void AddsResponseDescription() + { + const string expectedCreatedDescription = "A new item was created"; + const string expectedBadRequestDescription = "Validation failed for the request"; + + var apiDescription = GetApiDescription( + [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = expectedCreatedDescription)] + [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)] + () => TypedResults.Created("https://example.com", new TimeSpan())); + + Assert.Equal(2, apiDescription.SupportedResponseTypes.Count); + + var createdResponseType = apiDescription.SupportedResponseTypes[0]; + + Assert.Equal(201, createdResponseType.StatusCode); + Assert.Equal(typeof(TimeSpan), createdResponseType.Type); + Assert.Equal(typeof(TimeSpan), createdResponseType.ModelMetadata?.ModelType); + Assert.Equal(expectedCreatedDescription, createdResponseType.Description); + + var createdResponseFormat = Assert.Single(createdResponseType.ApiResponseFormats); + Assert.Equal("application/json", createdResponseFormat.MediaType); + + var badRequestResponseType = apiDescription.SupportedResponseTypes[1]; + + Assert.Equal(400, badRequestResponseType.StatusCode); + Assert.Equal(typeof(void), badRequestResponseType.Type); + Assert.Equal(typeof(void), badRequestResponseType.ModelMetadata?.ModelType); + Assert.Equal(expectedBadRequestDescription, badRequestResponseType.Description); + } + + [Fact] + public void WithEmptyMethodBody_AddsResponseDescription() + { + const string expectedCreatedDescription = "A new item was created"; + const string expectedBadRequestDescription = "Validation failed for the request"; + + var apiDescription = GetApiDescription( + [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = expectedCreatedDescription)] + [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)] + () => new InferredJsonClass()); + + Assert.Equal(3, apiDescription.SupportedResponseTypes.Count); + + var rdfInferredResponseType = apiDescription.SupportedResponseTypes[0]; + + Assert.Equal(200, rdfInferredResponseType.StatusCode); + Assert.Equal(typeof(InferredJsonClass), rdfInferredResponseType.Type); + Assert.Equal(typeof(InferredJsonClass), rdfInferredResponseType.ModelMetadata?.ModelType); + + var rdfInferredResponseFormat = Assert.Single(rdfInferredResponseType.ApiResponseFormats); + Assert.Equal("application/json", rdfInferredResponseFormat.MediaType); + Assert.Null(rdfInferredResponseType.Description); // There is no description set for the default "200" code, so we expect it to be null. + + var createdResponseType = apiDescription.SupportedResponseTypes[1]; + + Assert.Equal(201, createdResponseType.StatusCode); + Assert.Equal(typeof(TimeSpan), createdResponseType.Type); + Assert.Equal(typeof(TimeSpan), createdResponseType.ModelMetadata?.ModelType); + Assert.Equal(expectedCreatedDescription, createdResponseType.Description); + + var createdResponseFormat = Assert.Single(createdResponseType.ApiResponseFormats); + Assert.Equal("application/json", createdResponseFormat.MediaType); + + var badRequestResponseType = apiDescription.SupportedResponseTypes[2]; + + Assert.Equal(400, badRequestResponseType.StatusCode); + Assert.Equal(typeof(InferredJsonClass), badRequestResponseType.Type); + Assert.Equal(typeof(InferredJsonClass), badRequestResponseType.ModelMetadata?.ModelType); + Assert.Equal(expectedBadRequestDescription, badRequestResponseType.Description); + + var badRequestResponseFormat = Assert.Single(badRequestResponseType.ApiResponseFormats); + Assert.Equal("application/json", badRequestResponseFormat.MediaType); + } + [Fact] public void AddsResponseFormatsForTypedResultWithoutReturnType() { diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs index 77316c3b18b5..a33f2220d33f 100644 --- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs +++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.Tests/Services/OpenApiDocumentService/OpenApiDocumentServiceTests.Responses.cs @@ -320,7 +320,7 @@ public async Task GetOpenApiResponse_WithEmptyMethodBody_UsesDescriptionSetByUse // Act builder.MapPost("/api/todos", [ProducesResponseType(StatusCodes.Status200OK, Description = expectedCreatedDescription)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL + [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)] () => { }); @@ -354,7 +354,7 @@ public async Task GetOpenApiResponse_UsesDescriptionSetByUser() // Act builder.MapPost("/api/todos", [ProducesResponseType(StatusCodes.Status200OK, Description = expectedCreatedDescription)] - [ProducesResponseType(StatusCodes.Status400BadRequest)] // Omitted, meaning it should be NULL + [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)] () => { return TypedResults.Ok(new Todo(1, "Lorem", true, DateTime.UtcNow)); }); // This code doesn't return Bad Request, but that doesn't matter for this test. From 38606e96f1ee5f7c3b1a34ce90aa0178fee15e30 Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Fri, 21 Feb 2025 15:08:27 +0100 Subject: [PATCH 4/7] Add more tests --- .../test/ApiResponseTypeProviderTest.cs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) diff --git a/src/Mvc/Mvc.ApiExplorer/test/ApiResponseTypeProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/ApiResponseTypeProviderTest.cs index b65ef5cc4846..c58b6ed4e27b 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/ApiResponseTypeProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/ApiResponseTypeProviderTest.cs @@ -186,6 +186,66 @@ public void GetApiResponseTypes_ReturnsResponseTypesFromApiConventionItem() }); } + [Fact] + public void GetApiResponseTypes_ReturnsDescriptionFromProducesResponseType() + { + // Arrange + + const string expectedOkDescription = "All is well"; + const string expectedBadRequestDescription = "Invalid request"; + const string expectedNotFoundDescription = "Something was not found"; + + var actionDescriptor = GetControllerActionDescriptor( + typeof(GetApiResponseTypes_ReturnsResponseTypesFromDefaultConventionsController), + nameof(GetApiResponseTypes_ReturnsResponseTypesFromDefaultConventionsController.DeleteBase)); + + actionDescriptor.Properties[typeof(ApiConventionResult)] = new ApiConventionResult(new[] + { + new ProducesResponseTypeAttribute(200) { Description = expectedOkDescription}, + new ProducesResponseTypeAttribute(400) { Description = expectedBadRequestDescription }, + new ProducesResponseTypeAttribute(404) { Description = expectedNotFoundDescription }, + }); + + var provider = GetProvider(); + + // Act + var result = provider.GetApiResponseTypes(actionDescriptor); + + // Assert + Assert.Collection( + result.OrderBy(r => r.StatusCode), + responseType => + { + Assert.Equal(200, responseType.StatusCode); + Assert.Equal(typeof(BaseModel), responseType.Type); + Assert.False(responseType.IsDefaultResponse); + Assert.Equal(expectedOkDescription, responseType.Description); + Assert.Collection( + responseType.ApiResponseFormats, + format => + { + Assert.Equal("application/json", format.MediaType); + Assert.IsType(format.Formatter); + }); + }, + responseType => + { + Assert.Equal(400, responseType.StatusCode); + Assert.Equal(typeof(void), responseType.Type); + Assert.False(responseType.IsDefaultResponse); + Assert.Empty(responseType.ApiResponseFormats); + Assert.Equal(expectedBadRequestDescription, responseType.Description); + }, + responseType => + { + Assert.Equal(404, responseType.StatusCode); + Assert.Equal(typeof(void), responseType.Type); + Assert.False(responseType.IsDefaultResponse); + Assert.Empty(responseType.ApiResponseFormats); + Assert.Equal(expectedNotFoundDescription, responseType.Description); + }); + } + [ApiConventionType(typeof(DefaultApiConventions))] public class GetApiResponseTypes_ReturnsResponseTypesFromDefaultConventionsController : ControllerBase { From 6fcf2743e1c557cdad0bd3d56426d273ee8073fa Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Tue, 4 Mar 2025 11:29:51 +0100 Subject: [PATCH 5/7] Set the description to the final one encountered and add tests --- .../EndpointMetadataApiDescriptionProvider.cs | 4 +- ...pointMetadataApiDescriptionProviderTest.cs | 38 +++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 819bae15e226..f53a1c18c324 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -375,8 +375,8 @@ private static void AddSupportedResponseTypes( apiResponseType.ApiResponseFormats.Add(defaultResponseFormat); } - // We set the Description to the first non-null value we find that matches the status code. - apiResponseType.Description ??= responseMetadataTypes.FirstOrDefault(x => x.StatusCode == apiResponseType.StatusCode && x.Description is not null)?.Description; + // We set the Description to the LAST non-null value we find that matches the status code. + apiResponseType.Description ??= responseMetadataTypes.LastOrDefault(x => x.StatusCode == apiResponseType.StatusCode && x.Type == apiResponseType.Type && x.Description is not null)?.Description; if (!supportedResponseTypes.Any(existingResponseType => existingResponseType.StatusCode == apiResponseType.StatusCode)) { diff --git a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs index 251200c39421..9a5233a709a1 100644 --- a/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs +++ b/src/Mvc/Mvc.ApiExplorer/test/EndpointMetadataApiDescriptionProviderTest.cs @@ -375,6 +375,44 @@ public void WithEmptyMethodBody_AddsResponseDescription() Assert.Equal("application/json", badRequestResponseFormat.MediaType); } + /// + /// Setting the description grabs the LAST description. + // To validate this, we add multiple ProducesResponseType to validate that it only grabs the LAST ONE. + /// + [Fact] + public void AddsResponseDescription_UsesLastOne() + { + const string expectedCreatedDescription = "A new item was created"; + const string expectedBadRequestDescription = "Validation failed for the request"; + + var apiDescription = GetApiDescription( + [ProducesResponseType(typeof(int), StatusCodes.Status201Created, Description = "First description")] // The first item is an int, not a timespan, shouldn't match + [ProducesResponseType(typeof(int), StatusCodes.Status201Created, Description = "Second description")] // Not a timespan AND not the final item, shouldn't match + [ProducesResponseType(typeof(TimeSpan), StatusCodes.Status201Created, Description = expectedCreatedDescription)] // This is the last item, which should match + [ProducesResponseType(StatusCodes.Status400BadRequest, Description = "First description")] + [ProducesResponseType(StatusCodes.Status400BadRequest, Description = expectedBadRequestDescription)] + () => TypedResults.Created("https://example.com", new TimeSpan())); + + Assert.Equal(2, apiDescription.SupportedResponseTypes.Count); + + var createdResponseType = apiDescription.SupportedResponseTypes[0]; + + Assert.Equal(201, createdResponseType.StatusCode); + Assert.Equal(typeof(TimeSpan), createdResponseType.Type); + Assert.Equal(typeof(TimeSpan), createdResponseType.ModelMetadata?.ModelType); + Assert.Equal(expectedCreatedDescription, createdResponseType.Description); + + var createdResponseFormat = Assert.Single(createdResponseType.ApiResponseFormats); + Assert.Equal("application/json", createdResponseFormat.MediaType); + + var badRequestResponseType = apiDescription.SupportedResponseTypes[1]; + + Assert.Equal(400, badRequestResponseType.StatusCode); + Assert.Equal(typeof(void), badRequestResponseType.Type); + Assert.Equal(typeof(void), badRequestResponseType.ModelMetadata?.ModelType); + Assert.Equal(expectedBadRequestDescription, badRequestResponseType.Description); + } + [Fact] public void AddsResponseFormatsForTypedResultWithoutReturnType() { From fad49230edea337c6b95f6212d9e22d6b3d688d7 Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Tue, 11 Mar 2025 10:25:39 +0100 Subject: [PATCH 6/7] Use local method as requested by PR comment --- .../EndpointMetadataApiDescriptionProvider.cs | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index f53a1c18c324..5c51c5ca3615 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -376,7 +376,7 @@ private static void AddSupportedResponseTypes( } // We set the Description to the LAST non-null value we find that matches the status code. - apiResponseType.Description ??= responseMetadataTypes.LastOrDefault(x => x.StatusCode == apiResponseType.StatusCode && x.Type == apiResponseType.Type && x.Description is not null)?.Description; + apiResponseType.Description ??= GetMatchingResponseTypeDescription(responseProviderMetadataTypes.Values, apiResponseType); if (!supportedResponseTypes.Any(existingResponseType => existingResponseType.StatusCode == apiResponseType.StatusCode)) { @@ -398,6 +398,22 @@ private static void AddSupportedResponseTypes( supportedResponseTypes.Add(defaultApiResponseType); } + + static string? GetMatchingResponseTypeDescription(IEnumerable responseMetadataTypes, ApiResponseType apiResponseType) + { + // We set the Description to the LAST non-null value we find that matches the status code. + string? matchingDescription = null; + foreach (var metadata in responseMetadataTypes) + { + if (metadata.StatusCode == apiResponseType.StatusCode && + metadata.Type == apiResponseType.Type && + metadata.Description is not null) + { + matchingDescription = metadata.Description; + } + } + return matchingDescription; + } } private static ApiResponseType CreateDefaultApiResponseType(Type responseType) From 636c009698ece402c8f7b3f4ec3bb67029793f93 Mon Sep 17 00:00:00 2001 From: Sander ten Brinke Date: Tue, 11 Mar 2025 10:25:56 +0100 Subject: [PATCH 7/7] Fix comment --- .../src/EndpointMetadataApiDescriptionProvider.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs index 5c51c5ca3615..c920ea01d8aa 100644 --- a/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs +++ b/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs @@ -375,7 +375,6 @@ private static void AddSupportedResponseTypes( apiResponseType.ApiResponseFormats.Add(defaultResponseFormat); } - // We set the Description to the LAST non-null value we find that matches the status code. apiResponseType.Description ??= GetMatchingResponseTypeDescription(responseProviderMetadataTypes.Values, apiResponseType); if (!supportedResponseTypes.Any(existingResponseType => existingResponseType.StatusCode == apiResponseType.StatusCode))