Jethro Cao
Jethro Cao

Reputation: 1050

How to selectively add OperationFilter to API endpoint in Swagger?

I have the following IOperationFilter class, which implements the authentication headers required for some endpoints in my API application:

public class AuthenticationHeadersFilter : IOperationFilter
{
    public void Apply(OpenApiOperation operation, OperationFilterContext context)
    {
        if (operation.Parameters == null)
            operation.Parameters = new List<OpenApiParameter>();

        operation.Parameters.Add(new OpenApiParameter
            {
                Name = "AccountName",
                In = ParameterLocation.Header,
                Required = true
            });

        operation.Parameters.Add(new OpenApiParameter
            {
                Name = "ApiKey",
                In = ParameterLocation.Header,
                Required = true
            });
    }
}

The above is added to my application's Swagger UI through the following in the ConfigureServices method of Startup.cs:

services.AddSwaggerGen(c =>
{
    // ...
    // other Swagger configurations
    // ... 
    c.OperationFilter<AuthenticationHeadersFilter>();
});

This works well, however I now also have some endpoints which I'd like to be documented/displayed by Swagger, but should be completely publicly accessible without requiring the user to supply the AccountName and ApiKey as headers in their API request. How can I accomplish this?

I found this Stack Overflow answer, but not sure if it can be adapted for my purpose here. I wasn't able to find any useful documentation regarding the OperationFilterContext class.

Any help is greatly appreciated.

Upvotes: 10

Views: 17284

Answers (2)

Aspram
Aspram

Reputation: 653

Possible solution could be to have an attribute like this

using System;

namespace some.namespace
{
    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
    public class SkipAuthenticationHeadersAttribute : Attribute
    {
    }
}

and use it in swagger AuthenticationHeadersFilter like this:

using System.Linq;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace some.namespace
{
    /// <summary>
    /// https://alexdunn.org/2018/06/29/adding-a-required-http-header-to-your-swagger-ui-with-swashbuckle/.
    /// </summary>
    public class AuthenticationHeadersFilter : IOperationFilter
    {
        /// <summary>
        /// Swagger: Creates new required http header.
        /// </summary>
        /// <param name="operation"> Open Api Operation.</param>
        /// <param name="context">Operation Filter Context.</param>
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            var globalAttributes = context.ApiDescription.ActionDescriptor.FilterDescriptors.Select(p => p.Filter);
            var controllerAttributes = context.MethodInfo?.DeclaringType?.GetCustomAttributes(true);
            var methodAttributes = context.MethodInfo?.GetCustomAttributes(true);
            var produceAttributes = globalAttributes
                .Union(controllerAttributes ?? throw new InvalidOperationException())
                .Union(methodAttributes)
                .OfType<SkipAuthenticationHeadersAttribute>()
                .ToList();

            if (produceAttributes.Count != 0)
            {
                return;
            }

            if (operation.Parameters == null)
            {
                operation.Parameters = new List<OpenApiParameter>();
            }

            operation.Parameters.Add(new OpenApiParameter
            {
               Name = "AccountName",
               In = ParameterLocation.Header,
               Required = true,
               Schema = new OpenApiSchema
               {
                   Type = "String",
               }
            });

            operation.Parameters.Add(new OpenApiParameter
            {
               Name = "ApiKey",
               In = ParameterLocation.Header,
               Required = true,
               Schema = new OpenApiSchema
               {
                   Type = "String",
               }
            });
        }
    }
}

So when you add the attribute to a controller action, the action is filtered out.

Upvotes: 9

Ivan Sveshnikov
Ivan Sveshnikov

Reputation: 384

There is SwaggerOperationFilter attribute specifically for this case.

You need to install package Swashbuckle.AspNetCore.Annotations and enable it:

// Startup.cs
services.AddSwaggerGen(c =>
{
    ...
    c.EnableAnnotations();
};

Then you can use it like [SwaggerOperationFilter(typeof(YourFilterClass))] instead of c.OperationFilter<YourFilterClass>();

Upvotes: 15

Related Questions