diff --git a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
index 82864654e936..fe0a8b0188b1 100644
--- a/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
+++ b/src/OpenApi/gen/XmlCommentGenerator.Emitter.cs
@@ -386,9 +386,15 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
}
if (methodComment.Parameters is { Count: > 0})
{
+ var requestBodyParameterName = context.Description.ParameterDescriptions
+ .FirstOrDefault(parameterDescription =>
+ parameterDescription.Source == Microsoft.AspNetCore.Mvc.ModelBinding.BindingSource.Body ||
+ parameterDescription.Source == Microsoft.AspNetCore.Mvc.ModelBinding.BindingSource.FormFile ||
+ parameterDescription.Source == Microsoft.AspNetCore.Mvc.ModelBinding.BindingSource.Form)
+ ?.ParameterDescriptor?.Name;
+
foreach (var parameterComment in methodComment.Parameters)
{
- var parameterInfo = methodInfo.GetParameters().SingleOrDefault(info => info.Name == parameterComment.Name);
var operationParameter = operation.Parameters?.SingleOrDefault(parameter => parameter.Name == parameterComment.Name);
if (operationParameter is not null)
{
@@ -400,12 +406,12 @@ public Task TransformAsync(OpenApiOperation operation, OpenApiOperationTransform
}
targetOperationParameter.Deprecated = parameterComment.Deprecated;
}
- else
+ else if (requestBodyParameterName is not null && string.Equals(parameterComment.Name, requestBodyParameterName, StringComparison.Ordinal))
{
var requestBody = operation.RequestBody;
if (requestBody is not null)
{
- requestBody.Description = parameterComment.Description;
+ requestBody.Description ??= parameterComment.Description;
if (parameterComment.Example is { } jsonString)
{
var content = requestBody?.Content?.Values;
diff --git a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
index dad4380e2f9a..13d3d4f5ea9a 100644
--- a/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
+++ b/src/OpenApi/test/Microsoft.AspNetCore.OpenApi.SourceGenerators.Tests/OperationTests.MinimalApis.cs
@@ -530,4 +530,64 @@ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
Assert.Equal("Property with only value documentation.", valueOnlyParam2.Description);
});
}
+
+ [Fact]
+ public async Task RequestBodyDescriptionUsesFromBodyParameterCommentNotLastParameter()
+ {
+ // Regression test for issue #65805
+ // When an endpoint has [FromBody] parameter followed by other parameters like [FromServices] or CancellationToken,
+ // the request body description should use the [FromBody] parameter's XML comment, not the last parameter's.
+ var source = """
+using System;
+using System.Threading;
+using System.Threading.Tasks;
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Mvc;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Microsoft.AspNetCore.Http;
+using Microsoft.AspNetCore.Http.HttpResults;
+
+var builder = WebApplication.CreateBuilder();
+builder.Services.AddOpenApi();
+var app = builder.Build();
+
+app.MapPost("/test", TestEndpoint.PostData);
+app.Run();
+
+public static class TestEndpoint
+{
+ ///
+ /// Process some sample input.
+ ///
+ /// Sample data provided by the user.
+ /// Logger for diagnostics and tracing.
+ /// Injected cancellation token.
+ /// The number the user supplied.
+ public static async Task> PostData(
+ [FromBody] SampleData data,
+ [FromServices] ILogger logger,
+ CancellationToken cancellation)
+ {
+ ArgumentNullException.ThrowIfNull(data);
+ logger.LogInformation("User supplied {Number} and {Text}", data.Number, data.Text);
+ await Task.Delay(1, cancellation).ConfigureAwait(false);
+ return TypedResults.Ok(data.Number);
+ }
+}
+
+public record SampleData(int Number, string Text);
+""";
+ var generator = new XmlCommentGenerator();
+ await SnapshotTestHelper.Verify(source, generator, out var compilation);
+ await SnapshotTestHelper.VerifyOpenApi(compilation, document =>
+ {
+ var postOperation = document.Paths["/test"].Operations[HttpMethod.Post];
+ var requestBody = postOperation.RequestBody;
+
+ Assert.NotNull(requestBody);
+ Assert.Equal("Sample data provided by the user.", requestBody.Description);
+ Assert.NotEqual("Injected cancellation token.", requestBody.Description);
+ });
+ }
}