-
Notifications
You must be signed in to change notification settings - Fork 10.3k
[release/7.0] Infer response metadata in RequestDelegateFactory #43961
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
Conversation
Hi @halter73. Please make sure you've updated the PR description to use the Shiproom Template. Also, make sure this PR is not marked as a draft and is ready-to-merge. To learn more about how to prepare a servicing PR click here. |
@halter73 do we not need to update the |
No. The following logic now prevents aspnetcore/src/Mvc/Mvc.ApiExplorer/src/EndpointMetadataApiDescriptionProvider.cs Lines 340 to 354 in 5f91f1b
This does mean that just like WithOpenApi(), ApiExplorer will still generate an "empty" 200 response in RDF reasonably makes no claim to be able to infer anything about the response in Compatibility is also why I was unable to delete the internal response type inference based on the return type in WithOpenApi() and ApiExplorer. There's a chance that these are still used in cases where RDF hasn't inferred |
@Pilchie should I send a tactics email for this? Or is still unnecessary since we haven't branched rc2 yet? |
@@ -193,6 +193,8 @@ private static OpenApiResponses GetOpenApiResponses(MethodInfo method, EndpointM | |||
foreach (var annotation in eligibileAnnotations) | |||
{ | |||
var statusCode = annotation.Key.ToString(CultureInfo.InvariantCulture); | |||
|
|||
// TODO: Use the discarded response Type for schema generation |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super nit: we don't need to have this in code since it's tracked in the issue locker and that's a lot more resilient than in-code TODOs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm glad there's an issue. I agree that's more important, but I think the comment is also fine. I don't want to rekick the build just for this at least.
|
||
if (returnType == typeof(string)) | ||
{ | ||
builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: null, statusCode: 200, PlaintextContentType)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: null, statusCode: 200, PlaintextContentType)); | |
builder.Metadata.Add(ProducesResponseTypeMetadata.CreateUnvalidated(type: typeof(string), statusCode: 200, PlaintextContentType)); |
Why null
over typeof(string)
here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I left a brief comment here but I immediately resolved it. Basically, the caller shouldn't care if internally we used a Utf8ContentHttpResult
and ReadOnlySpan<byte>
or string
. It has no observable impact on the API, so string
doesn't have any impact on the schema. "text\plain" should be sufficient.
/// <summary> | ||
/// Gets or sets the type of the value returned by an action. | ||
/// </summary> | ||
public Type Type { get; set; } | ||
public Type? Type { get; set; } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure making this nullable is the right call here. Can you clarify your reasoning as to why this is?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This type is internal. The interface is public, and Type
is already nullable there. Everything reading Type
is using the interface is dealing with the possible null anyway for custom interface implementations which is why I didn't need to react.
Assert.True(defaultOperation.Content.ContainsKey("text/plain")); | ||
|
||
var annotatedOperation = operation.Responses["201"]; | ||
// Produces doesn't special case string?? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What do you mean by this comment?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was just surprised when updating this test that .Produces<string>(201);
creates "application/json" metadata. I would have expected "text\plain", but this is existing behavior. You can still explicitly set the content-type if you want.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wait it does? That seems like a bug.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, this has been behavior for a while. The Produces
extension method sets the type to application/json
by default unless explicitly defined.
We had some extensive conversations about this when we were building out the defaults and decided that setting the default to application/json
would be sensible for most minimal cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
#43979. If we agree it's important, I'll try to get a PR out by tomorrow before branching. I'm also going to open an API proposal about making Produces
and Accepts
work with groups. I think we should probably just retarget these from RouteHandlerBuilder
to IEndopintConventionBuilder
, but we can consider RouteGroupBuilder
-specific overloads although that's less extensible.
if it’s ready to go by end of day tomorrow I can approve. We are branch on Thursday. |
Approved for .NET 7 RC2. |
RequestDelegateFactory should infer metadata about the response bodies it produces similar to the way it infers metadata about the request bodies it consumes.
Description
RequestDelegateFactory already infers IAcceptsMetadata when it's reading JSON request bodies or multipart form data, so it's peculiar that it does not infer IProducesResponseTypeMetadata when our TypedResults do.
With this PR, RequestDelegateFactory now infers and adds "application/json" and "text/plain" ProducesResponseTypeMetadata with the appropriate return Type (for POCOs, not for
string
).Fixes #43675
Customer Impact
@DamianEdwards mentioned:
Regression?
This is a brand-new scenario though.
Risk
This mostly impacts the new
WithOpenApi()
andMapGroup()
features.Verification
Packaging changes reviewed?
@DamianEdwards @captainsafia @Pilchie