I'm using the default swagger implementation with my dotnet core webapi.
I would like the actual version number to appear instead of the template whenever version number is changing from the "select a definition" dropdown on top right corner.
So, for example when v2 is selected from the dropdown, it should appear like this
instead of this,
Any clues how this can be done?
You can use Microsoft.AspNetCore.Mvc.Versioning and custom swagger filter.
Create custom document filter;
public class ReplaceVersionWithExactValueInPathFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
var paths = swaggerDoc.Paths;
swaggerDoc.Paths = new OpenApiPaths();
foreach (var path in paths)
{
var key = path.Key.Replace("v{version}", swaggerDoc.Info.Version);
var value = path.Value;
swaggerDoc.Paths.Add(key, value);
}
}
}
Add versioning and swagger services to your service collection;
services.AddApiVersioning(config =>
{
// Specify the default API Version
config.DefaultApiVersion = new ApiVersion(1, 0);
// If the client hasn't specified the API version in the request, use the default API version number
config.AssumeDefaultVersionWhenUnspecified = true;
// Advertise the API versions supported for the particular endpoint
config.ReportApiVersions = true;
});
//Adding swagger services
services.AddSwaggerGen(options =>
{
//other configurations
options.SwaggerDoc("v1.0", new OpenApiInfo
{
Version = "v1.0",
Title = "Your Api Title",
Description = "Your Api Description",
TermsOfService = new Uri("https://yourcompany.com"),
Contact = new OpenApiContact { Name = "Your Company", Email = "info#yourcompany.com", Url = new Uri("https://yourcompany.com") },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
});
options.SwaggerDoc("v2.0", new OpenApiInfo
{
Version = "v2.0",
Title = "Your Api Title",
Description = "Your Api Description",
TermsOfService = new Uri("https://yourcompany.com"),
Contact = new OpenApiContact { Name = "Your Company", Email = "info#yourcompany.com", Url = new Uri("https://yourcompany.com") },
License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
});
options.DocumentFilter<ReplaceVersionWithExactValueInPathFilter>();
//other configurations
});
Register Swagger and SwaggerUI middleware;
app.UseSwagger(c =>
{
c.SerializeAsV2 = true;
c.RouteTemplate = "api/docs/{documentName}/docs.json";
}).UseSwaggerUI(c =>
{
c.SwaggerEndpoint($"/api/docs/v1.0/docs.json", "v1.0");
c.SwaggerEndpoint($"/api/docs/v2.0/docs.json", "v2.0");
});
Then configure your controller;
[Route("api/v{version:apiVersion}/[controller]")]
[ApiController]
public class TestsController : ControllerBase
{
[HttpPost("Test")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "v1.0")]
public IActionResult TestV1()
{
return Ok();
}
[HttpPost("Test")]
[ApiVersion("2.0")]
[ApiExplorerSettings(GroupName = "v2.0")]
public IActionResult TestV2()
{
return Ok();
}
}
If you want to remove version header from your document you should use this;
public class VersionHeaderFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters != null)
{
var versionParameter = operation.Parameters.SingleOrDefault(p => p.Name == "version");
if (versionParameter != null)
operation.Parameters.Remove(versionParameter);
}
}
}
services.AddSwaggerGen(options =>
{
//other configurations
options.OperationFilter<VersionHeaderFilter>();
//other configurations
});
This should work.
Related
I configure my swagger like this:
services.AddSwaggerGen(
options =>
{
options.SwaggerDoc(
IntegrationApiVersion,
new OpenApiInfo { Title = IntegrationApiName, Version = IntegrationApiVersion });
options.SwaggerDoc(
ApplicationApiVersion,
new OpenApiInfo { Title = ApplicationApiName, Version = ApplicationApiVersion });
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "Bearer Token: e.g. \"Bearer <your token here>\"",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer",
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
}
});
options.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
options.EnableAnnotations();
options.SchemaFilter<SmartEnumSchemaFilter>();
options.SupportNonNullableReferenceTypes();
options.UseAllOfToExtendReferenceSchemas();
options.IncludeXmlComments(
Path.Combine(AppContext.BaseDirectory, $"{Assembly.GetExecutingAssembly().GetName().Name}.xml"),
includeControllerXmlComments: true);
options.OperationFilter<TestOperationFilter>();
})
.AddFluentValidationRulesToSwagger();
But since only 1 of my documents requires authentication IntegrationApiVersion, I want to hide the 'Authorize' button for the other doc. I found that the call to AddSecutityDefinition is what adds the button, but it does not let me define an api name/version, and I can't figure out how to set that definition from a filter
I managed to get the little locks to display, and JWT auth works great by adding this filter:
public class TestOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (context.DocumentName == SwaggerConfiguration.IntegrationApiVersion)
{
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header
},
Array.Empty<string>()
}
}
};
}
}
}
But I can't figure out how to apply the same solution for the 'Authorize' button itself, so that it only appears on my IntegrationApiVersion page
The closes thing I could find was people doing this, but using something called NonBodyParameter to add a SecurityDefinition inside a filter, but it seems that type is no longer available to me. Does anyone have any advice?
Using SwashBuckle 6.3.0
Security definition gets add at the document level so you have to modify the document.
here I have written a document filter which removes the schema only for this document.
public class SwaggerDocumentFilter : IDocumentFilter
{
public void Apply(OpenApiDocument swaggerDoc, DocumentFilterContext context)
{
if(context.DocumentName == SwaggerConfiguration.ApplicationApiVersion)
{
swaggerDoc.Components.SecuritySchemes.Remove("Bearer");
}
}
}
hope it is helpful
In Asp.NET MVC I was using Filters according below: (It works fine)
public class TracerAttribute : ActionFilterAttribute
{
public override Task OnActionExecutingAsync(HttpActionContext actionContext, CancellationToken cancellationToken)
{
return Task.Factory.StartNew(() =>
{
var path = ConfigurationManager.AppSettings["QueueAddress"];
var queue = new MessageQueue(path);
queue.DefaultPropertiesToSend.Recoverable = true;
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var body = TraceMessageHelper.BuildBody(actionContext, assembly);
try
{
var label = ConfigurationManager.AppSettings["MessageLabel"] + body.Timestamp.ToString("yyyyMMddHHmmss");
var message = new Message
{
Formatter = new XmlMessageFormatter(new Type[] { typeof(TraceMessage) }),
Label = label,
Body = body
};
queue.Send(message);
}
catch (Exception e)
{
var logger = LogManager.GetLogger("LogInFile");
logger.Warn(e, LogMessageHelper.FormatRequest("TRACE SEND FAILED", actionContext.Request));
if (body != null)
{
var tracerlogger = LogManager.GetLogger("TracerInFile");
tracerlogger.Info(JsonConvert.SerializeObject(body));
}
}
queue.Close();
});
}
}
}
In Asp.NET CORE I'm using Filters according below: (It doesn't Work)
public class TracerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
{
var path = "FormatName:Direct=TCP:013cdnt2305\\private$\\TracerQueue";
var queue = new MessageQueue(path);
queue.DefaultPropertiesToSend.Recoverable = true;
var assembly = System.Reflection.Assembly.GetExecutingAssembly();
var body = TraceMessageHelper.BuildBody(actionContext, assembly);
try
{
var label = "APR_USER_20191018132324";
var message = new Message
{
Formatter = new XmlMessageFormatter(new Type[] { typeof(TraceMessage) }),
Label = label,
Body = body
};
queue.Send(message);
}
catch (Exception e)
{
HttpRequestMessageFeature hreqmf = new HttpRequestMessageFeature(actionContext.HttpContext);
var logger = LogManager.GetLogger("LogInFile");
logger.Warn(e, LogMessageHelper.FormatRequest("TRACE SEND FAILED", hreqmf.HttpRequestMessage));
if (body != null)
{
var tracerlogger = LogManager.GetLogger("TracerInFile");
tracerlogger.Info(JsonConvert.SerializeObject(body));
}
}
queue.Close();
}
}
}
}
In ASP.net Core I need to do change using only one parameter
In Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddControllersWithViews();
services.AddRazorPages();
services.AddMvc(config =>
{
config.Filters.Add(new TracerAttribute());
});
//use our filter as a service type on the Action or Controller level
//services.AddScoped<TracerAttribute>();
}
In ASP.NET MVC (Works Fine)
In ASP.NET CORE(Doesn't work)
Angular return for both:
Anyone help me how to fix it?
If the angular app post json data(Content-Type: application/json) to .net core server side , you can get the values via [FromBody] :
[TracerAttribute]
[HttpPost]
public IActionResult MyAction([FromBody]Mymodel mymodel)
{
}
When action filter is calling , the body stream already has been read and [FromBody] model has been populated. Then you can get the data via :
public class TracerAttribute : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext actionContext)
{
var modelValues = actionContext.ActionArguments["mymodel"] as Mymodel ;
}
}
I am trying to implement the version options on a MVC dotnet Core app that has API endpoint on it.
The set up i am after is like this
--AiM api
|_v1
|_v2
--RMS api
|_v1
I have it mostly working but the items on v1 are not showing up on v2. The output is like so
But when we get to the version 2 on the AiM v2 endpoint I only the one item
Which is not what i was expecting
I have made a test to get each one showing on its different pages in swagger like this
In controller
[ApiVersion("2.0")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "aim_v1")]
[Route("aim/v{version:apiVersion}/write/")]
public class aimWriter_v1Controller : Controller
{
[SwaggerOperation(Tags = new[] { "AiM Departments" })]
[HttpPost("departments/delete/{id}")]
public IActionResult departments(string foo)
{
return Json(new
{
results = "edited"
});
}
[SwaggerOperation(Tags = new[] { "AiM Contacts" })]
[HttpPost("contacts/delete/{id}")]
public IActionResult contact_delete(string foo)
{
return Json(new
{
results = "edited"
});
}
[SwaggerOperation(Tags = new[] { "AiM Contacts" })]
[HttpPost("contacts/activate/{id}")]
public IActionResult contact_activate(string foo)
{
return Json(new
{
results = "edited"
});
}
}
[ApiVersion("2.0")]
[ApiExplorerSettings(GroupName = "aim_v2")]
[Route("aim/v{version:apiVersion}/write/")]
public class aimWriter_v2Controller : Controller
{
[SwaggerOperation(Tags = new[] { "AiM Contacts" })]
[HttpPost("contacts/delete/{id}")]
public IActionResult contact_delete(string foo)
{
return Json(new
{
results = "edited"
});
}
}
[ApiVersion("2.0")]
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "aim_v1")]
[Route("aim/v{version:apiVersion}/")]
public class aim_v1Controller : Controller
{
[SwaggerOperation(Tags = new[] { "AiM Rooms" })]
[HttpPost("rooms")]
public IActionResult rooms(string foo)
{
return Json(new
{
results = "foo"
});
}
[SwaggerOperation(Tags = new[] { "AiM Buildings" })]
[HttpPost("buildings/rooms/{id}")]
public IActionResult building_rooms(string foo)
{
return Json(new
{
results = "foo"
});
}
[SwaggerOperation(Tags = new[] { "AiM Rooms" })]
[HttpPost("rooms/{id}")]
public IActionResult room(string foo)
{
return Json(new
{
results = "foo"
});
}
}
// set up as just a new endpoint (NOTE: in different controller)
[ApiVersion("1.0")]
[ApiExplorerSettings(GroupName = "rms_v1")]
[Route("rms/v{version:apiVersion}/")]
public class rms_v1Controller : Controller
{
[SwaggerOperation(Tags = new[] { "RMS Orders" })]
[HttpPost("set_order/{id}")]
public IActionResult set_order(string foo)
{
return Json(new
{
results = "foo"
});
}
}
And in the Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddRouting(options => options.LowercaseUrls = true);
services.AddMvc();
services.AddApiVersioning(options => {
options.AssumeDefaultVersionWhenUnspecified = true ;
options.DefaultApiVersion = new ApiVersion(new DateTime(2016, 7, 1));
});
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("aim_v1", new Info
{
Version = "aim/v1",
Title = "WSU HTTP API"
});
c.SwaggerDoc("aim_v2", new Info
{
Version = "aim/v2",
Title = "WSU HTTP API v2"
});
c.SwaggerDoc("rms_v1", new Info
{
Version = "rms/v1",
Title = "WSU HTTP API"
});
//Set the comments path for the swagger json and ui.
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var xmlPath = Path.Combine(basePath, "project.in.bin.def.xml");
c.IncludeXmlComments(xmlPath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
// Enable middleware to serve generated Swagger as a JSON endpoint.
app.UseSwagger(o =>
{
o.PreSerializeFilters.Add((swaggerDoc, httpReq) => swaggerDoc.Host = httpReq.Host.Value);
o.RouteTemplate = "doc/{documentName}/scheme.json";
});
// Enable middleware to serve swagger-ui (HTML, JS, CSS etc.), specifying the Swagger JSON endpoint.
app.UseSwaggerUI(c =>
{
c.RoutePrefix = "docs";
c.SwaggerEndpoint("/doc/aim_v1/scheme.json", "AiM v1.0.0");
c.SwaggerEndpoint("/doc/rms_v1/scheme.json", "Rms v1.0.0");
c.SwaggerEndpoint("/doc/aim_v2/scheme.json", "AiM v2.0.0");
});
}
And in the index.html for the swagger ui doc template file has
<script type="text/javascript">
window.JSConfig = JSON.parse('{"SwaggerEndpoints":[{"Url":"/doc/aim_v1/scheme.json","Description":"AiM v1.0.0"},{"Url":"/doc/aim_v2/scheme.json","Description":"AiM v2.0.0"},{"Url":"/doc/rms_v1/scheme.json","Description":"RMS v1.0.0"}],"BooleanValues":["false","true"],"DocExpansion":"list","SupportedSubmitMethods":["get","post","put","delete","patch"],"OnCompleteScripts":[],"OnFailureScripts":[],"ShowRequestHeaders":false,"JsonEditor":false,"OAuth2ClientId":"your-client-id","OAuth2ClientSecret":"your-client-secret-if-required","OAuth2Realm":"your-realms","OAuth2AppName":"your-app-name","OAuth2ScopeSeparator":" ","OAuth2AdditionalQueryStringParams":{}}');
$(function () {
hljs.configure({
highlightSizeThreshold: 5000
});
// Pre load translate...
if(window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
window.swaggerUi = new SwaggerUi({
url: "/doc/aim_v1/scheme.json",
dom_id: "swagger-ui-container",
supportedSubmitMethods: ['get', 'post'],
onComplete: function(swaggerApi, swaggerUi){
if(typeof initOAuth == "function") {
initOAuth({
clientId: "ffff==",
clientSecret: "bbbb",
realm: "wsu-api",
appName: "wsu-api-broker",
scopeSeparator: " ",
additionalQueryStringParams: {}
});
}
if(window.SwaggerTranslator) {
window.SwaggerTranslator.translate();
}
_.each(JSConfig.OnCompleteScripts, function (script) {
$.getScript(script);
});
},
onFailure: function(data) {
log("Unable to Load SwaggerUI");
},
docExpansion: false,
jsonEditor: false,
defaultModelRendering: 'schema',
showRequestHeaders: false
});
window.swaggerUi.load();
function log() {
if ('console' in window) {
console.log.apply(console, arguments);
}
}
});
In order to get the items on the different endpoints I used the [ApiExplorerSettings(GroupName = "aim_v1")] on the classes and matched them up in the Startup.cs and index.html files. At this point I am unsure where to make my edit to get all of the [ApiVersion("1.0")] items show on the [ApiVersion("2.0")] as I think the ApiExplorerSettings GroupName is what it locking this up.
To integrate everything smoothly, you also need to add the official API Explorer package for API Versioning. This will collate all of the API version information for you in a way that Swagger will understand. The official Swagger/Swashbuckle integration wiki topic has additional details and examples.
The setup will look like:
public void ConfigureServices( IServiceCollection services )
{
// note: this option is only necessary when versioning by url segment.
// the SubstitutionFormat property can be used to control the format of the API version
services.AddMvcCore().AddVersionedApiExplorer(
options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = true;
} );
services.AddMvc();
services.AddApiVersioning();
services.AddSwaggerGen(
options =>
{
var provider = services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
foreach ( var description in provider.ApiVersionDescriptions )
{
options.SwaggerDoc( description.GroupName, CreateInfoForApiVersion( description ) );
}
options.IncludeXmlComments( XmlCommentsFilePath );
} );
}
public void Configure( IApplicationBuilder app, IHostingEnvironment env, IApiVersionDescriptionProvider provider )
{
app.UseMvc();
app.UseSwagger();
app.UseSwaggerUI(
options =>
{
foreach ( var description in provider.ApiVersionDescriptions )
{
options.SwaggerEndpoint( $"/swagger/{description.GroupName}/swagger.json", description.GroupName.ToUpperInvariant() );
}
} );
}
static string XmlCommentsFilePath
{
get
{
var basePath = PlatformServices.Default.Application.ApplicationBasePath;
var fileName = typeof( Startup ).GetTypeInfo().Assembly.GetName().Name + ".xml";
return Path.Combine( basePath, fileName );
}
}
static Info CreateInfoForApiVersion( ApiVersionDescription description )
{
var info = new Info()
{
Title = $"Sample API {description.ApiVersion}",
Version = description.ApiVersion.ToString(),
Description = "A sample application with Swagger, Swashbuckle, and API versioning.",
Contact = new Contact() { Name = "Bill Mei", Email = "bill.mei#somewhere.com" },
TermsOfService = "Shareware",
License = new License() { Name = "MIT", Url = "https://opensource.org/licenses/MIT" }
};
if ( description.IsDeprecated )
{
info.Description += " This API version has been deprecated.";
}
return info;
}
A full working answer is in that question:
Grouping and Versioning not working well together in swagger in asp.net core 3.1 web api
As the author said, the DocInclusionPredicate in AddSwaggerGen in the ConfigureServices is doing the trick to map the proper controller to the wanted swagger file.
How to include email in the claim and get the email value via the API Controller?
IdentityServer4 Sample: https://github.com/IdentityServer/IdentityServer4.Samples/tree/release/Quickstarts/8_EntityFrameworkStorage
API IdentityController https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/8_EntityFrameworkStorage/src/Api/Controllers/IdentityController.cs
TestUser https://github.com/IdentityServer/IdentityServer4.Samples/blob/release/Quickstarts/8_EntityFrameworkStorage/src/QuickstartIdentityServer/Quickstart/TestUsers.cs
When you define an api resource (have a look in Config.cs), you can do that :
new ApiResource
{
Name = "api",
DisplayName = "My API",
UserClaims =
{
JwtClaimTypes.Id,
JwtClaimTypes.Subject,
JwtClaimTypes.Email
}
}
It defines that your API will receive those claims.
EDIT :
It's better if you add the associate resource's to the GetIdentityResources function (see Config.cs)
Have a glance in the offical documentation to have a better picture
http://docs.identityserver.io/en/release/topics/resources.html .
I give you a complete example from a personal project:
public static IEnumerable<IdentityResource> GetIdentityResources()
{
//>Declaration
var lIdentityResources = new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Email()
};
//>Processing
foreach (var lAPIResource in GetApiResources())
{
lIdentityResources.Add(new IdentityResource(lAPIResource.Name,
lAPIResource.UserClaims));
}
//>Return
return lIdentityResources;
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new ApiResource
{
Name = "api1",
DisplayName = "api1 API",
UserClaims =
{
JwtClaimTypes.Id,
JwtClaimTypes.Subject,
JwtClaimTypes.Email
}
}
};
}
I searched for possible ways to add a request header parameter that would be added automatically to every method in my web-api but i couldn't find a clear one.
While searching i found that the method OperationFilter() has to do something about it.
What the user "G T" wrote is correct but it is not working with Swagger 5. We have some new changes:
From: Operation to: OpenApiOperation
From: IParameter to: OpenApiParameter
From: NonBodyParameter to: OpenApiParameter, and the most important is...
From: Type = "string" to: Schema = new OpenApiSchema { Type = "String" }
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace MyAPI
{
public class AuthorizationHeaderParameterOperationFilter: IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var filterPipeline = context.ApiDescription.ActionDescriptor.FilterDescriptors;
var isAuthorized = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is AuthorizeFilter);
var allowAnonymous = filterPipeline.Select(filterInfo => filterInfo.Filter).Any(filter => filter is IAllowAnonymousFilter);
if (isAuthorized && !allowAnonymous)
{
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = "Authorization",
In = ParameterLocation.Header,
Description = "access token",
Required = true,
Schema = new OpenApiSchema
{
Type = "string",
Default = new OpenApiString("Bearer ")
}
});
}
}
}
}
And in Startup => ConfigureServices => services.AddSwaggerGen()
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
Yes you can do it via inheriting from IOperationFilter
You can find the answer on GitHub here: AddRequiredHeaderParameter
using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;
public class AddRequiredHeaderParameter : IOperationFilter
{
public void Apply(Operation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<IParameter>();
operation.Parameters.Add(new NonBodyParameter
{
Name = "X-User-Token",
In = "header",
Type = "string",
Required = false
});
}
}
Then you go to your SwaggerConfig.cs file and add the following in the AddSwaggerGen section:
c.OperationFilter<AddRequiredHeaderParameter>();
Rebuild, and enjoy.
Another way to add custom headers is by adding parameters into controller action.
The following example will add x-test parameter to the UI:
[HttpPost]
public IActionResult Test([FromHeader(Name="x-test")][Required] string requiredHeader)
{
return Ok();
}
I have improved the respectful Wille Esteche's answer a bit.
If you want to apply headers not to all methods, but only to your selected controller methods, you can use attributes.
[HttpPost]
[Route(nameof(Auth))]
[SwaggerHeader(Constants.HeaderDomainSid, "Encrypted User.Sid got from client", "abc123", true)]
public ActionResult<string> Auth([FromHeader(Name = Constants.HeaderDomainSid)] string headerDomainSid = null)
{ .....
Attribute class:
public class SwaggerHeaderAttribute : Attribute
{
public string HeaderName { get; }
public string Description { get; }
public string DefaultValue { get; }
public bool IsRequired { get; }
public SwaggerHeaderAttribute(string headerName, string description = null, string defaultValue = null, bool isRequired = false)
{
HeaderName = headerName;
Description = description;
DefaultValue = defaultValue;
IsRequired = isRequired;
}
}
Filter:
public class SwaggerHeaderFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters ??= new List<OpenApiParameter>();
if (context.MethodInfo.GetCustomAttribute(typeof(SwaggerHeaderAttribute)) is SwaggerHeaderAttribute attribute)
{
var existingParam = operation.Parameters.FirstOrDefault(p =>
p.In == ParameterLocation.Header && p.Name == attribute.HeaderName);
if (existingParam != null) // remove description from [FromHeader] argument attribute
{
operation.Parameters.Remove(existingParam);
}
operation.Parameters.Add(new OpenApiParameter
{
Name = attribute.HeaderName,
In = ParameterLocation.Header,
Description = attribute.Description,
Required = attribute.IsRequired,
Schema = string.IsNullOrEmpty(attribute.DefaultValue)
? null
: new OpenApiSchema
{
Type = "String",
Default = new OpenApiString(attribute.DefaultValue)
}
});
}
}
}
For Asp .Net MVC 5 you can use.
Following the need to be done in Swagger Config file.
private class AddAuthorizationHeaderParameter: IOperationFilter // as a nested class in script config file.
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
operation.parameters.Add(new Parameter
{
name = "Authorization",
#in = "header",
type = "string",
required = true
});
}
}
c.OperationFilter<AddAuthorizationHeaderParameter>(); // finally add this line in .EnableSwagger
You can also add any no of headers for header implementation in Swagger.
In my case (.NET 5) I have to change some :
using System.Collections.Generic;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
public class AddRequiredHeaderParameter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter()
{
Name = "userNr",
In = ParameterLocation.Header,
Required = true
});
operation.Parameters.Add(new OpenApiParameter()
{
Name = "periodNo",
In = ParameterLocation.Header,
Required = true
});
}
}
and in Startup.cs --> ConfigureServices --> AddSwaggerGen
add
c.OperationFilter<AddRequiredHeaderParameter>();
If swagger is used in ASP.Net MVC5, and required to add headers to get input from swagger UI.
Create a class inherited from IOperationFilter:
using Swashbuckle.Swagger;
using System.Collections.Generic;
using System.Web.Http.Description;
public class AddHeaderParameters : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
operation.parameters.Add(new Parameter
{
name = "AccountUserName",
#in = "header",
type = "string",
required = true,
//description = "Account username"
});
}
}
Give reference of this class in SwaggerConfig.cs inside Configuration.EnableSwagger as:
c.OperationFilter<AddHeaderParameters>();
Important thing to note that the header name supposed to match with the actual header you have created for API.
This works for Swashbucke.AspNetCore 5.6.3
Create a new file and add the code below to the file
using System.Collections.Generic;
using System.Linq;
using Microsoft.AspNetCore.Mvc.Authorization;
using Microsoft.OpenApi.Any;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace YourNameSpace
{
public class AuthorizationHeaderParameterOperationFilter:IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (operation.Security == null)
operation.Security = new List<OpenApiSecurityRequirement>();
var scheme = new OpenApiSecurityScheme { Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "bearer" } };
operation.Security.Add(new OpenApiSecurityRequirement
{
[scheme] = new List<string>()
});
}
}
}
In your Startup.cs add the code below to the ConfigureServices under services.AddSwaggerGen()
c.AddSecurityDefinition("bearer", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
BearerFormat = "JWT",
In = ParameterLocation.Header,
Scheme = "bearer"
});
c.OperationFilter<AuthorizationHeaderParameterOperationFilter>();
All should work fine now
for more information check here
Also you may have a base model class and use attribute [FromHeader] for properties which should be sent in custom headers. Something like this:
public class AuthenticatedRequest
{
[FromHeader(Name = "User-Identity")]
public string UserIdentity { get; set; }
}
At least it works fine for ASP.NET Core 2.1 and Swashbuckle.AspNetCore 2.5.0.
Another customized implementation for:
Web API (.Net 5)
Swashbuckle.AspNetCore.Swagger (6.2.3)
Swashbuckle.AspNetCore.SwaggerGen (6.2.3)
Swashbuckle.AspNetCore.SwaggerUI (6.2.3)
Following some of the answers from this thread did get me a required field for Authorization. However I had run into a different problems. I need to have the followings fixed:
Display a lock sign beside API actions where authentication is required. For anonymous actions there is no point having a required Authorization field.
For every API endpoi I did not want to input the Authorization key. It becomes a redundant work if we are to test couple of APIs at one go. So I needed a single point where I would put the Auth key and execute the APIs from the Swagger UI. The Swagger UI would handle the Auth part where required.
I did not want to add custom filter or codes in the controller actions where I might have to edit many actions.
Last but not the least, I had a problem where I did get the required Authorization field on the Swagger UI but that was not being post back in request header along the other API fields.
To overcome the above issues I have done the followings:
Create a IOperationFilter type filter to indicate which API endpoints requires authentication and which ones are anonymous type
A button on the Swagger UI to bring a popup to input my Auth token that would be used automatically with the API calls from the Swagger UI
Here are the codes:
#Step-1: The custom IOperationFilter type filrer:
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (context.ApiDescription.ActionDescriptor is ControllerActionDescriptor descriptor)
{
// If [AllowAnonymous] is not applied or [Authorize] or Custom Authorization filter is applied on either the endpoint or the controller
if (!context.ApiDescription.CustomAttributes().Any((a) => a is AllowAnonymousAttribute)
&& (context.ApiDescription.CustomAttributes().Any((a) => a is AuthorizeAttribute)
|| descriptor.ControllerTypeInfo.GetCustomAttribute<AuthorizeAttribute>() != null))
{
if (operation.Security == null)
operation.Security = new List<OpenApiSecurityRequirement>();
operation.Security.Add(
new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme
{
Name = "Authorization",
In = ParameterLocation.Header,
BearerFormat = "Bearer token",
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[]{ }
}
});
}
}
}
Then in the startup.cs file, within the ConfigureServices method add the filter like this:
services.AddSwaggerGen(options =>
{
...
options.OperationFilter<AddRequiredHeaderParameter>();
...
...
}
Doing the above will add an icon to the API endpoints where Authentication is required. Here is the result:
#Step-2: Then we need the Auth token input UI. Add the following code right after the line in the startup.cs where you have added the IOperationFilter filter:
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme()
{
Name = "Authorization",
Type = SecuritySchemeType.Http,
Scheme = "Bearer",
BearerFormat = "JWT",
In = ParameterLocation.Header,
Description = "JWT Authorization header. \r\n\r\n Enter the token in the text input below.,
});
This will get you a Authorize button at the top of the API descriptor page. Clicking the button will bring a popup window where you can input the Auth token and have it passed down with each API call.
Not sure if it was answered with the 2021 Sep release for .net 5.0 but I took Rami'es answer and adapted it to the following:
namespace PartnerLicense
{
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
using Walter.Web.FireWall.Reseller;
public class AddRequiredHeaderParameter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters.Add(new()
{
Description = "Add your reseller ID here",
Name = HeaderKeys.ResellerId,
In = ParameterLocation.Header,
Required = true
});
}
}
}