Reputation: 1050
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
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
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