dotnet / aspnetcore

ASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.
https://asp.net
MIT License
35.43k stars 10.02k forks source link

Support emitting OpenAPI documents in YAML format #58516

Open john-shika opened 2 weeks ago

john-shika commented 2 weeks ago

Is there an existing issue for this?

Is your feature request related to a problem? Please describe the problem.

I am trying to do configuration OpenApi with YAML format, some research, it can be tricky, because need access to Microsoft.OpenApi document instead of Microsoft.AspNetCore.OpenApi extension, that know is not accessing by public (internal sealed class), must be rewrite entire OpenApi extension with self-version or waiting some feature.

Describe the solution you'd like

I am supposed to try this one, but some restricted access some object classes, warning in my IDE the object classes is internal sealed class, so I give my idea, in the comment below

public static IEndpointConventionBuilder MapOpenApi(this IEndpointRouteBuilder endpoints, [StringSyntax("Route")] string pattern = OpenApiConstants.DefaultOpenApiRoute, OpenApiFormat format = OpenApiFormat.Json)
    {
        var options = endpoints.ServiceProvider.GetRequiredService<IOptionsMonitor<OpenApiOptions>>();
        return endpoints.MapGet(pattern, async (HttpContext context, string documentName = OpenApiConstants.DefaultDocumentName) =>
            {
                var documentService = context.RequestServices.GetKeyedService<OpenApiDocumentService>(documentName);
                if (documentService is null)
                {
                    context.Response.StatusCode = StatusCodes.Status404NotFound;
                    context.Response.ContentType = "text/plain;charset=utf-8";
                    await context.Response.WriteAsync($"No OpenAPI document with the name '{documentName}' was found.");
                }
                else
                {
                    var document = await documentService.GetOpenApiDocumentAsync(context.RequestServices, context.RequestAborted);
                    var documentOptions = options.Get(documentName);
                    using var output = MemoryBufferWriter.Get();
                    using var writer = Utf8BufferTextWriter.Get(output);
                    try
                    {
                        switch (format)
                        {
                            case OpenApiFormat.Json:
                                document.Serialize(new OpenApiJsonWriter(writer), documentOptions.OpenApiVersion);
                                context.Response.ContentType = "application/json;charset=utf-8";
                                await context.Response.BodyWriter.WriteAsync(output.ToArray(), context.RequestAborted);
                                await context.Response.BodyWriter.FlushAsync(context.RequestAborted);
                                break;
                            case OpenApiFormat.Yaml:
                                document.Serialize(new OpenApiYamlWriter(writer), documentOptions.OpenApiVersion);
                                context.Response.ContentType = "text/yaml;charset=utf-8";
                                await context.Response.BodyWriter.WriteAsync(output.ToArray(), context.RequestAborted);
                                await context.Response.BodyWriter.FlushAsync(context.RequestAborted);
                                break;
                            default:
                                throw new ArgumentOutOfRangeException(nameof(format), format, null);
                        }
                    }
                    finally
                    {
                        MemoryBufferWriter.Return(output);
                        Utf8BufferTextWriter.Return(writer);
                    }

                }
            }).ExcludeFromDescription();
    }

this is method yoink origin method from Microsoft.AspNetCore.OpenApi.Extensions

Additional context

Oh Yap, I am trying added Bearer JWT from samples in Microsoft.OpenApi, it works but must be complicated because need security Bearer for each-endpoint, make some extensions and parsing tags instead of Authorize attribute class because there not parsing and exists in OpenApiDocument, trigger some tag have JWT and added security bearer for specific endpoints, but this is bad look

ex. code like this one

    [HttpGet("validate")]
    [Tags(TagNames.BearerJwt, TagNames.RoleAdmin, "Auth")]
    [EndpointSummary("VALIDATE_USER")]
    [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "Admin")]
    [Produces(MediaTypeNames.Application.Json)]
    public async Task<IResult> ValidateToken([FromHeader(Name = "Authorization")] string authorization)
   {
      ...
   }

I must be added Authorize and Tag TagNames.BearerJwt for trigger my extension to create security bearer for specific endpoints, why not added Authorize attribute information in OpenApiDocument so I can create security bearer with easy ways :)

but thanks for reading my problems, and I am appreciating your hard works, If there are any mistakes from me, please forgive me, because this is my first time using Microsoft.AspNetCore.OpenApi.

captainsafia commented 1 week ago

@john-shika Thanks for filing this issue! It was originally requested in the OpenAPI meta-issue but I never got around to implementing it.

I've opened https://github.com/dotnet/aspnetcore/pull/58616 with a proposed implementation. Let me know what you think about it?