How to access custom class attribute

Hi,

I have tried for hours to access this Class attribute but it does not appear to exist. I am unable to figure out how to access it from the OperationFilter.

my goal is to have this schema:

"MyClass": {
      "get": {
                  "x-displayName": "MyClass"
     }
}

Can anyone help?


I am using Swashbuckle.AspNetCore and ASP.Net Core mapped endpoint (i.e. options.MapEndpoints(true, true, UseSystemJson.Never))

I created this custom attribute:

[AttributeUsage(AttributeTargets.Class, Inherited = true, AllowMultiple = false)]
public class OpenApiDisplayNameAttribute : Attribute
{
    public string DisplayName { get; set;}
}

Added it to a class:

[OpenApiDisplayName(nameof(MyClass))]
[Route("/MyClass", "GET")]
public class MyClass
{
}

Added it my SwaggerGen:

services.AddSwaggerGen(options =>
{
   ..
  options.OperationFilter<OpenApiDisplayNameOperationFilter>();
  ..
}

and created a OperationFilter:

public class OpenApiDisplayNameOperationFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        
        //This does not work--always NULL
        var descriptor = context.ApiDescription.ActionDescriptor as Microsoft.AspNetCore.Mvc.Controllers.ControllerActionDescriptor;

        //This doesn't work either--always NULL
        var attribute = context.MethodInfo?.GetCustomAttribute<OpenApiDisplayNameAttribute>();

        if (attribute != null)
        {        
               operation.AddExtension("x-displayName", new OpenApiString(attribute.DisplayName));
        }
    }
}

ASP.NET Core Endpoints don’t know the Reqeust DTO they map to, but you could do a reverse lookup to get the Request Type from ServiceStack’s HostContext.Metadata e.g:

public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
    var requestType = HostContext.Metadata.FindRoute(
        "/" + context.ApiDescription.RelativePath, context.ApiDescription.HttpMethod ?? "GET")?.RequestType;
    if (requestType != null)
    {
        var attr = requestType.FirstAttribute<OpenApiDisplayNameAttribute>();
        if (attr != null)
        {
            operation.AddExtension("x-displayName", new OpenApiString(attr.DisplayName));
        }
    }
}

Alternatively you could attach the metadata to the endpoint with a RouteHandlerBuilder, e.g:

app.UseServiceStack(new AppHost(), options =>
{
    options.RouteHandlerBuilders.Add((routeBuilder, operation, verb, route) =>
    {
        var attr = operation.RequestType.FirstAttribute<OpenApiDisplayNameAttribute>();
        if (attr != null)
            routeBuilder.WithMetadata(attr);
    });
    options.MapEndpoints();
});

Where you’d then be able to look it up by endpoint, e.g:

public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
    if (context.ApiDescription.ActionDescriptor.EndpointMetadata.FirstOrDefault(x =>
            x is OpenApiDisplayNameAttribute) is OpenApiDisplayNameAttribute attr)
    {        
        operation.AddExtension("x-displayName", new OpenApiString(attr.DisplayName));
    }
}
1 Like

That worked perfectly–thank you!