Add header parameter to endpoint OpenAPI definition

3 weeks ago 17
ARTICLE AD BOX

I have an IBindableFromHttpContext<CurrentLanguage> endpoint parameter that I am parsing from language header.

Problem is that it does not show up on OpenAPI/Swagger as a header parameter field.

I need Accept-Language header parameter to show up for endpoints that have CurrentLanguage Parameter

What I have tried

I believe for that CurrentLanguage should implement IEndpointParameterMetadataProvider and add corresponding metadata, but I have been unable to do so.

Just adding builder.Metadata.Add(new FromHeaderAttribute { Name = "Accept-Language" }); does nothing.

I also tried adding LanguageParameterMetadata(ParameterInfo parameter) : IParameterBindingMetadata metadata but thats no-go either, since that interface contains ParameterInfo parameter, which has a protected contructor and CustomAttributes are not editable.

Minimal repro with problem:

using Microsoft.AspNetCore.Mvc; using System.Reflection; var builder = WebApplication.CreateBuilder(args); builder.Services.AddSwaggerGen(); var app = builder.Build(); app.UseSwagger(); app.UseSwaggerUI(); app.MapGet("/test", (CurrentLanguage language, [FromHeader] string testHeader) => language.Value.Code); app.Run(); record CurrentLanguage(Language Value) : IBindableFromHttpContext<CurrentLanguage> { public static ValueTask<CurrentLanguage?> BindAsync(HttpContext context, ParameterInfo parameter) { var langValue = context.Request.Headers.AcceptLanguage.FirstOrDefault() ?? "en"; var language = new Language(langValue); return ValueTask.FromResult(new CurrentLanguage(language))!; } } record Language(string Code);

What fixes the problem, but does not solve it - adding WithOpenApi:

app.MapGet("/test", (CurrentLanguage language, [FromHeader] string testHeader) => language.Value.Code) .WithOpenApi(operation => { operation.Parameters.Add(new OpenApiParameter { Name = "Accept-Language", In = ParameterLocation.Header, Required = false, Description = "Language preference (e.g. en-US, ka-GE)", Schema = new OpenApiSchema { Type = JsonSchemaType.String } }); return operation; });

Cons:

Deprecated Must be applied manually to each endpoint

Stealing ParameterInfo from other header parameter:

record CurrentLanguage(Language Value) : IBindableFromHttpContext<CurrentLanguage>, IEndpointParameterMetadataProvider { public static ValueTask<CurrentLanguage?> BindAsync(HttpContext context, ParameterInfo parameter) { var langValue = context.Request.Headers.AcceptLanguage.FirstOrDefault() ?? "en"; var language = new Language(langValue); return ValueTask.FromResult(new CurrentLanguage(language))!; } public static void PopulateMetadata(ParameterInfo parameter, EndpointBuilder builder) { var p = (IParameterBindingMetadata)builder.Metadata[3]; var m = new LanguageParameterBindingMetadata("Accept-Language", p.HasTryParse, p.HasBindAsync, p.ParameterInfo, p.IsOptional); builder.Metadata.Add(m); } } class LanguageParameterBindingMetadata(string name, bool hastryParse, bool hasBindAsync, ParameterInfo parameterInfo, bool isOptonal) : IParameterBindingMetadata { public string Name => name; public bool HasTryParse => hastryParse; public bool HasBindAsync => hasBindAsync; public ParameterInfo ParameterInfo => parameterInfo; public bool IsOptional => isOptonal; }

Cons: obviously a non-solution

Read Entire Article