-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
Support returning OpenAPI document in YAML from MapOpenApi #58616
Conversation
if (UseYaml(pattern)) | ||
{ | ||
document.Serialize(new OpenApiYamlWriter(writer), documentOptions.OpenApiVersion); | ||
context.Response.ContentType = "application/yaml;charset=utf-8"; |
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.
RFC 9512 declares application/yaml
as the official content-type for YAML-based types. One thing I observed is that this MIME type is fairly new and most browsers still don't know how to render it inline and will fallback to downloading the YAML file to disk.
To get around this, we could use the text/plain+yaml
content type to force inline rendering while still being in compliance with the MIME type spec for YAML.
Side note: This RFC is in draft which defines an official application/openapi
content type that we might consider using in the future.
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.
Based on my limited knowledge of MIME types, application/yaml
seems more correct since it is primarily for programmatic consumption, but I can definitely imagine people asking for text+yaml
as a convenience. I see one of the RFC authors was from Mozilla, so hopefully built-in support isn't too far off?
@@ -29,11 +29,13 @@ public void MapOpenApi_ReturnsEndpointConventionBuilder() | |||
Assert.IsAssignableFrom<IEndpointConventionBuilder>(returnedBuilder); | |||
} | |||
|
|||
[Fact] | |||
public void MapOpenApi_SupportsCustomizingPath() | |||
[Theory] |
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.
Do any of these tests validate it is actually YAML? They check the content type and that a OpenAPI parser can parse the content, but if you replaced the writer implementation in the YAML block with the JSON one would they still pass?
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, it would still pass if the writer used was a JSON one. Since JSON is valid YAML, if we wanted to verify that the emitted content was actually YAML we'd have to perhaps do some string-based check to see if it contained the correct starting characters compared to a JSON output?
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.
TIL JSON is YML, but that still feels wrong to me that the tests wouldn't detect the content not being the requested format.
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.
Seems sensible, but might have a bug tail. Getting it in early makes sense.
if (UseYaml(pattern)) | ||
{ | ||
document.Serialize(new OpenApiYamlWriter(writer), documentOptions.OpenApiVersion); | ||
context.Response.ContentType = "application/yaml;charset=utf-8"; |
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.
Based on my limited knowledge of MIME types, application/yaml
seems more correct since it is primarily for programmatic consumption, but I can definitely imagine people asking for text+yaml
as a convenience. I see one of the RFC authors was from Mozilla, so hopefully built-in support isn't too far off?
context.Response.ContentType = "application/json;charset=utf-8"; | ||
if (UseYaml(pattern)) | ||
{ | ||
document.Serialize(new OpenApiYamlWriter(writer), documentOptions.OpenApiVersion); |
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 assume OpenApiYamlWriter
does smart things to guard against, e.g., the Norway Problem?
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.
OpenAPI recommends YAML 1.2 of the spec, which prohibits non-boolean string literals from being interpreted as booleans, AFAIK.
The serialization itself seems to emit it as a string correctly. For example:
[JsonConverter(typeof(JsonStringEnumConverter<Choices>))]
public enum Choices
{
Yes,
No
}
becomes
schemas:
Choices:
enum:
- Yes
- No
} | ||
else | ||
{ | ||
document.Serialize(new OpenApiJsonWriter(writer), documentOptions.OpenApiVersion); |
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.
Are people going to be surprised when openapi.toml
returns JSON, rather than failing?
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.
Maybe give more control for the user, passing all values in params, and give some magic extension .json or .yaml in the end of path if exists or not or wrong will be removed and set the right extension, for content type / mime type pass in the last param like mimeType: | default value like pattern: "openapi/v1" format: JSON mimeType: "application/json" ( 0-0)/
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.
Are people going to be surprised when openapi.toml returns JSON, rather than failing?
Depends on user expectation and the type of experience that we want to provide. TOML isn't a supported OpenAPI document format so we could decide to throw if we encounter an extension and it's not associated with the supported set of formats.
- Route with extension ending in
.yaml
oryml
produces an OpenAPI document in YAML format. - Route with extension ending in
.json
produces an OpenAPI document in JSON format. - Route with extension not ending in any of the above produces an error.
- Route with no extension produces an OpenAPI document in JSON format.
With this behavior, #3 would be a breaking change between .NET 9 and .NET 10 but I think we can live with it.
Maybe give more control for the user, passing all values in params, and give some magic extension .json or .yaml in the end of path if exists or not or wrong will be removed and set the right extension, for content type / mime type pass in the last param like mimeType: | default value like pattern: "openapi/v1" format: JSON mimeType: "application/json" ( 0-0)/
Yeah, I considered this option as well but opted to be more conservative in the amount of new API that was introduced. It also feels that given there are only two formats supported currently, we might want to support a simpler API for this.
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've opted to avoid the breaking change for now. We've got runaway to revisit this if needed.
/azp run LGTM |
Commenter does not have sufficient privileges for PR 58616 in repo dotnet/aspnetcore |
Thanks! I owe another commit to this branch that improves the tests based on the feedback in #58616 (comment). I'm inclined to leave the implementation as is for now (anything that isn't YAML will produce JSON) and avoid the extra logic in around what extension is actually in the file. We're early enough in the release cycle that we can actually change this. |
* Support returning OpenAPI document in YAML from MapOpenApi * Update tests and YAML content type
Addresses #58516.
This PR adds support for returning the OpenAPI document at runtime in the YAML format. It does this by checking the extension on the route pattern that is provided for resolving the OpenAPI document. If a user calls:
Then visiting the
/openapi/v1.yaml
route in their application will return the document in YAML format. The implementation treats routes ending in eitheryaml
oryml
as YAML-generating by default. All other routes will produce an OpenAPI document serialized to JSON.As an alternative approach, we could follow the pattern proposed in this issue and introduce an additional
OpenApiFormat
argument to theMapOpenApi
method. However, because the implementation assumes that the extension appears in the route pattern by default we run the risk of users inadvertently calling:Which would use the default route pattern for the document that uses a JSON file extension with the incorrect file format. I figured it was more straightforward if we define the format based on the route pattern and avoid adding another option. There is a risk that more formats get added in the future and the complexity of our checks increases but that isn't a likely eventuality at the moment.