Skip to content

Make HttpContext available to OpenAPI transformers #56189

@martincostello

Description

@martincostello

Background and Motivation

I was looking at how to wire up the servers property based on the current HttpContext like NSwag does after opening #56188, and noticed that the HttpContext isn't immediately available to any OpenAPI transformers.

It can be easily be made available via IHttpContextAccessor, but that's often frowned upon from a performance perspective, so I figured it would be worth raising the possibility of making it available from the request pipeline to avoid the need to do that.

The HttpContext could be passed through into OpenApiDocumentService.GetOpenApiDocumentAsync() here:

var document = await documentService.GetOpenApiDocumentAsync(context.RequestAborted);

Then it can be directly assigned into the various context objects passed to any transformers, as well as being available to the document service itself.

Proposed API

namespace Microsoft.AspNetCore.OpenApi;

public sealed partial class OpenApiDocumentTransformerContext
{
+   /// <summary>
+   /// Gets the HTTP context associated with the current HTTP request.
+   /// </summary>
+   public required HttpContext { get; init }
}

public sealed partial class OpenApiOperationTransformerContext
{
+   /// <summary>
+   /// Gets the HTTP context associated with the current HTTP request.
+   /// </summary>
+   public required HttpContext { get; init }
}

public sealed partial class OpenApiSchemaTransformerContext
{
+   /// <summary>
+   /// Gets the HTTP context associated with the current HTTP request.
+   /// </summary>
+   public required HttpContext { get; init }
}

Usage Examples

internal sealed class AddServersTransformer : IOpenApiDocumentTransformer
{
    /// <inheritdoc/>
    public Task TransformAsync(
        OpenApiDocument document,
        OpenApiDocumentTransformerContext context,
        CancellationToken cancellationToken)
    {
        document.Servers = [new() { Url = GetServerUrl(context) }];
        return Task.CompletedTask;
    }

    private static string GetServerUrl(OpenApiDocumentTransformerContext context)
    {
        var request = context.HttpContext.Request;

        var scheme = TryGetFirstHeader("X-Forwarded-Proto") ?? request.Scheme;
        var host = TryGetFirstHeader("X-Forwarded-Host") ?? request.Host.ToString();

        return new Uri($"{scheme}://{host}").ToString().TrimEnd('/');

        string? TryGetFirstHeader(string name)
            => request.Headers.TryGetValue(name, out var values) ? values.FirstOrDefault() : null;
    }
}

Alternative Designs

None.

Risks

None?

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-suggestionEarly API idea and discussion, it is NOT ready for implementationarea-minimalIncludes minimal APIs, endpoint filters, parameter binding, request delegate generator etcarea-mvcIncludes: MVC, Actions and Controllers, Localization, CORS, most templatesfeature-openapi

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions