How can I add individual headers on different controllers.
E.g.:
Controller Name: Controller1,
Custom header: Header1
Controller Name: Controller2,
Custom header: Header2
The headers should be displayed for all the apis under the specific controller
This can be solved by adding an OperationFilter to your swagger configuration.
First you have to provide a class that implements IOperationFilter. The Applymethod receives an Operation parameter which contains the controller name in the tagfield. When the Swagger UI is rendered, the Applymethod will be called for each method in the API. You could even provide individual parameters for each API method, as Operation also contains the operationId.
public class AddRequiredHeaderParameter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
if (operation.parameters == null)
operation.parameters = new List<Parameter>();
if (operation.tags[0]?.CompareTo("Example") == 0)
{
operation.parameters.Add(new Parameter
{
name = "X-ExampleParam",
#in = "header",
#default = "42", // optional default value, can be omitted
type = "string",
description = "My special parameter for the example API",
required = true
});
}
else if (operation.tags[0]?.CompareTo("Whatever") == 0)
{
// add other header parameters here
}
}
}
In the debugger, with a controller named ExampleController, it looks like this:
The result in the Swagger UI is a special parameter that is only applied to the API of my Example controller:
Tell Swagger to use your OperationFilter by adding one line in the Register method of the SwaggerConfig class:
public class SwaggerConfig
{
public static void Register(HttpConfiguration config)
{
var thisAssembly = typeof(SwaggerConfig).Assembly;
//GlobalConfiguration.Configuration
config
.EnableSwagger(c =>
{
... // omitted some lines here
c.OperationFilter<AddRequiredHeaderParameter>(); // Add this line
... // omitted some lines here
})
}
The idea to this solution is based on ShaTin's answer: How to send custom headers with requests in Swagger UI?
This is not an answer but stackoverflow wont' let me just make a comment on the solution from jps. Just wanted to add this is what I needed for my using clauses in jps's answer to get this to work in regular .net:
using Swashbuckle.Application;
using Swashbuckle.Swagger;
using System.Collections.Generic;
using System.Web.Http.Description;
Related
i use asp.net boilerplate for my project. i updated devexpress version from 21.1.4 to 21.2.5 and made a custom WebDocumentViewerController.
public class CustomWebDocumentController :
WebDocumentViewerController
{
public
CustomWebDocumentController(IWebDocumentViewerMvcControllerService
controllerService) : base(controllerService)
{
}
}
i used this code to remove defualt DocumentViewerController in startup.sc:
services.AddMvc()
.ConfigureApplicationPartManager(x =>
{
var parts = x.ApplicationParts;
var aspNetCoreReportingAssemblyName =
typeof(WebDocumentViewerController).Assembly.GetName().Name;
var reportingPart = parts.FirstOrDefault(part => part.Name
== aspNetCoreReportingAssemblyName);
if (reportingPart != null)
{
parts.Remove(reportingPart);
}
});
the code is running but the defualtcontroller is still in list of controllers and makes swagger confiused.
how should i remove the defualt contoller?
thanks for your time.
The reason why this "ambiguous HTTP method for Action Error" pops up is because this controller 'CustomWebDocumentController' is missing the HTTP action decoration ([HttpGet],[HttpPost] etc) on top of it.
Simply decorate the controller with '[ApiExplorerSettings(IgnoreApi=true)]'. This will ultimately cause the entire controller or individual action to be omitted from the Swagger output.
Source: Exclude controllers methods from docs without using the Obsolete attribute
public class CustomWebDocumentController : WebDocumentViewerController
{
[ApiExplorerSettings(IgnoreApi = true)]
public
CustomWebDocumentController(IWebDocumentViewerMvcControllerService controllerService) : base(controllerService)
{
}
}
So I decided to use this method since it's the simplest to add custom headers for my api calls, example:
[HttpPost]
[Route("something")]
public async Task<somethingObject> DoSomething([Microsoft.AspNetCore.Mvc.FromHeader(Name = "deviceGuid")] string deviceGuid)
{
var somethingObject= await doSomethingWithDevice(deviceGuid)
return somethingObject;
}
The expected outcome from this is a field in Swagger where I can input the deviceGuid and Swagger should consider it as a header.
Issue at hand is that Swagger is considering it a query and not a header:
Any clue how I can solve this?
I dont think SwashBuckle (im guessing this is the swagger implementation you're using), knows about FromHeader.
What you could do, is this;
Make an OperationFilter that changes the ParameterType to Header - something like the following; - note I found this in this gist.
public class FromHeaderAttributeOperationFilter : IOperationFilter
{
public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription)
{
foreach (var httpParameterDescriptor in apiDescription.ActionDescriptor.GetParameters().Where(e => e.GetCustomAttributes<FromHeaderAttribute>().Any()))
{
var parameter = operation.parameters.Single(p => p.name == httpParameterDescriptor.ParameterName);
parameter.name = httpParameterDescriptor.GetCustomAttributes<FromHeaderAttribute>().Single().HeaderName;
parameter.#in = "header";
}
}
}
As I was adding swagger to my API, I wanted to get default values and response examples. I added the NuGet packages and tried to follow this tutorial. The SwaggerResponseExample attribute works properly but the SwaggerRequestExample seems to be simply ignored.
With my action defined as follow
[SwaggerRequestExample(typeof(int), typeof(PersonExamples.Request))]
[SwaggerResponseExample(200, typeof(PersonExamples.Response))]
/* more attribute & stuff */
public IActionResult Get(int id) { /* blabla */ }
The PersonExamples class being defined as follow (non-revelant code removed)
public class PersonExamples
{
public class Request : IExamplesProvider
{
public object GetExamples() { return _persons.List().First().Id; }
}
public class Response : IExamplesProvider
{
public object GetExamples() { return _persons.List().First(); }
}
}
Here is also the relevant part of the Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSwaggerGen(conf =>
{
conf.SwaggerDoc(_documentationPrefix, new Info
{
Title = "Global",
Version = "v0",
});
conf.OperationFilter<ExamplesOperationFilter>();
var filePath = Path.Combine(PlatformServices.Default.Application.ApplicationBasePath, "Global.xml");
conf.IncludeXmlComments(filePath);
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseSwagger(opt =>
{
opt.RouteTemplate = "{documentName}/swagger.json";
});
if (env.IsDevelopment())
{
app.UseSwaggerUI(conf =>
{
conf.SwaggerEndpoint($"/{_documentationPrefix}/swagger.json", "DataService API");
conf.RoutePrefix = "doc/swagger";
});
}
}
When I run my project and go to the swagger.json page I notice that the response example is properly written, but the request example is nowhere to be found. After further debugging, I notice that a breakpoint placed in PersonExamples.Response.GetExamples will be hit when the page is called, but one placed in the PersonExamples.Request.GetExamples method won't. So i believe that the SwaggerRequestExample attribute never calls the method and may not even be called itself.
Did I improperly used the tag ? Why is it never called ?
I know this question is quite old, but Swagger Examples don't support GET request parameters (query parameters). It only works when the parameters are in the request body (eg: POST requests)
I'm trying to set up Swagger for our API using Swashbuckle. Every one of our controllers inherits from a BaseController that has ~8 parameters(userId, language, platform, etc) on it. They each have read only variables with their get function set to read the value from the headers pulled in. Ex:
protected int UserID
{
get
{
return int.Parse(HttpContext.Current.Request.Headers["user_Id"]);
}
}
When I first installed Swagger, it would only give options to pass in parameters that are listed in the method. Those parameters always exclude the ~8 parameters that are set in BaseController, so if a controller uses the user ID, the user ID isn't listed in the method signature. As user ID, won't show up on the Swagger-generated UI.
Right now, I've been experimenting using the SwaggerOperation attribute like so:
[SwaggerOperation("UserValidate(UserID)")]
public int UserValidate()
{
return this._UserValidateService.UserValidate(this.UserID);
And I have a custom swagger parameter that checks all of the methods if their SwaggerOperation name contains "UserID", and if so add the UserID header to that call.
My problem with this is that I have to manually go through every call and add the swagger operation, and if the call ever changes, we have to remember to also change the SwaggerOperation.
I also thought about just adding the 8 to every API call and marking them as optional, but that sounds like it would bloat the UI horribly.
Please advise on the situation. Are there options I haven't considered? Are they Swashbuckle functionalities I've missed that will fix my issues?
Thanks in advance.
If you are using swashbuckle>=5, you can add operation filter as below. It will add a header to only selected requests:
public class AddHeaderParameter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (context.ApiDescription.HttpMethod.Equals("POST") && context.ApiDescription.RelativePath.Contains("login"))
{
if (operation.Parameters == null)
operation.Parameters = new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = "UserId",
In = ParameterLocation.Header,
Schema = new OpenApiSchema
{
Type = "String",
},
Required = true
});
}
}
Above adds a required header t only POST operation which has action name as login.
I am currently using swagger in my project and i have more than 100 controllers there. I guess due to the large number of controller, swagger UI documentation page takes more than 5 min to load its controller. Is it possible to select specific controllers at the UI page and load options for them only?
Or else there are other methods to load UI page faster?
Help me!
you can use ApiExplorerSettings on either controller to ignore a controller completely or on a method.
[ApiExplorerSettings(IgnoreApi = true)]
public class MyController
{
[ApiExplorerSettings(IgnoreApi = true)]
public string MyMethod
{
...
}
}
Using swashbuckle's document filter you can remove some elements of the generated specification after the fact, and they would then not be included on the integrated swagger-ui. Create a class such as the below:
using System;
using System.Web.Http.Description;
using Swashbuckle.Swagger;
internal class SwaggerFilterOutControllers : IDocumentFilter
{
void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
foreach (ApiDescription apiDescription in apiExplorer.ApiDescriptions)
{
Console.WriteLine(apiDescription.Route.RouteTemplate);
if ((apiDescription.RelativePathSansQueryString().StartsWith("api/System/"))
|| (apiDescription.RelativePath.StartsWith("api/Internal/"))
|| (apiDescription.Route.RouteTemplate.StartsWith("api/OtherStuff/"))
)
{
swaggerDoc.paths.Remove("/" + apiDescription.Route.RouteTemplate.TrimEnd('/'));
}
}
}
}
and then edit your SwaggerConfig.cs file to include the filter:
GlobalConfiguration.Configuration
.EnableSwagger(c =>
c.DocumentFilter<SwaggerFilterOutControllers>();
Note that while the controllers have been removed from the specification, other items such as the result models will still be included in the specification and might still be slowing down the page load.
It could also be slow simply due to enumerating all of the controllers/models etc in the first place, in which case this might not help.
Edit: I noticed it would regenerate the whole definition every time the UI page was viewed (which could be crippling in your scenario). Fortunately it's super easy to cache this (which should be fine as it shouldn't change at runtime for the majority of people).
Add this to your config:
c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
and use this class shamelessly copied from https://github.com/domaindrivendev/Swashbuckle/blob/master/Swashbuckle.Dummy.Core/App_Start/CachingSwaggerProvider.cs
using Swashbuckle.Swagger;
using System.Collections.Concurrent;
namespace <your namespace>
{
public class CachingSwaggerProvider : ISwaggerProvider
{
private static ConcurrentDictionary<string, SwaggerDocument> _cache =
new ConcurrentDictionary<string, SwaggerDocument>();
private readonly ISwaggerProvider _swaggerProvider;
public CachingSwaggerProvider(ISwaggerProvider swaggerProvider)
{
_swaggerProvider = swaggerProvider;
}
public SwaggerDocument GetSwagger(string rootUrl, string apiVersion)
{
string cacheKey = string.Format("{0}_{1}", rootUrl, apiVersion);
return _cache.GetOrAdd(cacheKey, (key) => _swaggerProvider.GetSwagger(rootUrl, apiVersion));
}
}
}
In response to the previous answer, this is the updated code for ASP.NET Core. I also added the feature to remove models.
using System;
using System.Linq;
using System.Web.Http;
using Swashbuckle.AspNetCore.SwaggerGen;
using Swashbuckle.AspNetCore.Swagger;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
internal class SwaggerFilterOutControllers : IDocumentFilter
{
void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, DocumentFilterContext context)
{
foreach (var item in swaggerDoc.Paths.ToList())
{
if (!(item.Key.ToLower().Contains("/api/endpoint1") ||
item.Key.ToLower().Contains("/api/endpoint2")))
{
swaggerDoc.Paths.Remove(item.Key);
}
}
swaggerDoc.Definitions.Remove("Model1");
swaggerDoc.Definitions.Remove("Model2");
}
}
swaggerDoc.paths.Remove("/" + apiDescription.Route.RouteTemplate.TrimEnd('/'));did not remove anything for me. So,
internal class SwaggerFilterOutControllers : IDocumentFilter
{
void IDocumentFilter.Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer)
{
foreach (var item in swaggerDoc.Paths.ToList())
{
if (!(item.Key.ToLower().Contains("/api/v1/xxxx") ||
item.Key.ToLower().Contains("/api/v1/yyyy")))
{
swaggerDoc.Paths.Remove(item.Key);
}
}
}
}
You can try this. You APIExplorerSetting to specify APIs to be included in a particular group.
Start by defining multiple Swagger docs in Startup.cs:
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "My API - V1",
Version = "v1"
});
c.SwaggerDoc("v2", new OpenApiInfo {
Title = "My API - V2",
Version = "v2"
});
});
Then decorate the individual controller with the above groups:
[ApiExplorerSettings(GroupName = "v2")]
Reference: https://github.com/domaindrivendev/Swashbuckle.AspNetCore#generate-multiple-swagger-documents
You can use DocInclusionPredicate to Customize the Action Selection Process:
When selecting actions for a given Swagger document, the generator invokes a DocInclusionPredicate against every ApiDescription that's surfaced by the framework. The default implementation inspects ApiDescription.GroupName and returns true if the value is either null OR equal to the requested document name. However, you can also provide a custom inclusion predicate.
services.AddSwaggerGen(c =>
{
c.DocInclusionPredicate((string title, ApiDescription apiDesc) =>
{
// filter the ApiDescription
});
});