How to send custom headers with requests in Swagge

2019-01-31 16:08发布

问题:

I have some endpoints in the API - /user/login, /products.

In Swagger UI I post email and password to /user/login and as a response I receive a token string.

Then, I can copy the token from the response and want to use it as Authorization header value in requests to all urls if it's present, and to /products as an example.

Should I create a text input manually somewhere on the Swagger UI page, then put the token there and somehow inject in the requests or are there tools to manage it in a better way?

回答1:

You can add a header parameter to your request, and Swagger-UI will show it as an editable text box:

swagger: "2.0"
info:
  version: 1.0.0
  title: TaxBlaster
host: taxblaster.com
basePath: /api
schemes:
- http

paths:

  /taxFilings/{id}:

    get:
      parameters:
      - name: id
        in: path
        description: ID of the requested TaxFiling
        required: true
        type: string
      - name: auth
        in: header
        description: an authorization header
        required: true
        type: string
      responses:
        200:
          description: Successful response, with a representation of the Tax Filing.
          schema:
            $ref: "#/definitions/TaxFilingObject"
        404:
          description: The requested tax filing was not found.

definitions:
  TaxFilingObject:
    type: object
    description: An individual Tax Filing record.
    properties:
      filingID:
        type: string
      year:
        type: string
      period:
        type: integer
      currency:
        type: string
      taxpayer:
        type: object

You can also add a security definition with type apiKey:

swagger: "2.0"
info:
  version: 1.0.0
  title: TaxBlaster
host: taxblaster.com
basePath: /api
schemes:
- http

securityDefinitions:
  api_key:
    type: apiKey
    name: api_key
    in: header
    description: Requests should pass an api_key header.

security: 
 - api_key: []

paths:

  /taxFilings/{id}:

    get:
      parameters:
      - name: id
        in: path
        description: ID of the requested TaxFiling
        required: true
        type: string

      responses:
        200:
          description: Successful response, with a representation of the Tax Filing.
          schema:
            $ref: "#/definitions/TaxFilingObject"
        404:
          description: The requested tax filing was not found.

definitions:
  TaxFilingObject:
    type: object
    description: An individual Tax Filing record.
    properties:
      filingID:
        type: string
      year:
        type: string
      period:
        type: integer
      currency:
        type: string
      taxpayer:
        type: object

The securityDefinitions object defines security schemes.

The security object (called "security requirements" in Swagger–OpenAPI), applies a security scheme to a given context. In our case, we're applying it to the entire API by declaring the security requirement a top level. We can optionally override it within individual path items and/or methods.

This would be the preferred way to specify your security scheme; and it replaces the header parameter from the first example. Unfortunately, Swagger-UI doesn't offer a text box to control this parameter, at least in my testing so far.



回答2:

On ASP.net WebApi, simplest way to pass-in a header on Swagger UI is to implement Apply(...) method on IOperationFilter interface.

Add this to your project:

public class AddRequiredHeaderParameter : 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 = "MyHeaderField",
            @in = "header",
            type = "string",
            description = "My header field",
            required = true
        });
    }
}

On SwaggerConfig.cs, register the filter from above using c.OperationFilter<T>():

    public static void Register()
    {
        var thisAssembly = typeof(SwaggerConfig).Assembly;

        GlobalConfiguration.Configuration 
            .EnableSwagger(c =>
            {
                c.SingleApiVersion("v1", "YourProjectName");
                c.IgnoreObsoleteActions();
                c.UseFullTypeNameInSchemaIds();
                c.DescribeAllEnumsAsStrings();
                c.IncludeXmlComments(GetXmlCommentsPath());
                c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());


                c.OperationFilter<AddRequiredHeaderParameter>(); // Add this here
            })
            .EnableSwaggerUi(c =>
            {
                c.DocExpansion(DocExpansion.List);
            });
    }


回答3:

In ASP.NET Core 2 Web API, using Swashbuckle.AspNetCore package 2.1.0, implement a IDocumentFilter:

SwaggerSecurityRequirementsDocumentFilter.cs

using System.Collections.Generic;
using Swashbuckle.AspNetCore.Swagger;
using Swashbuckle.AspNetCore.SwaggerGen;

namespace api.infrastructure.filters
{
    public class SwaggerSecurityRequirementsDocumentFilter : IDocumentFilter
    {
        public void Apply(SwaggerDocument document, DocumentFilterContext context)
        {
            document.Security = new List<IDictionary<string, IEnumerable<string>>>()
            {
                new Dictionary<string, IEnumerable<string>>()
                {
                    { "Bearer", new string[]{ } },
                    { "Basic", new string[]{ } },
                }
            };
        }
    }
}

In Startup.cs, configure a security definition and register the custom filter:

public void ConfigureServices(IServiceCollection services)
{
    services.AddSwaggerGen(c =>
    {
        // c.SwaggerDoc(.....

        c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
        {
            Description = "Authorization header using the Bearer scheme",
            Name = "Authorization",
            In = "header"
        });

        c.DocumentFilter<SwaggerSecurityRequirementsDocumentFilter>();
    });
}

In Swagger UI, click on Authorize button and set value for token.

Result:

curl -X GET "http://localhost:5000/api/tenants" -H "accept: text/plain" -H "Authorization: Bearer ABCD123456"


回答4:

Also it's possible to use attribute [FromHeader] for web methods parameters (or properties in a Model class) which should be sent in custom headers. Something like this:

[HttpGet]
public ActionResult Products([FromHeader(Name = "User-Identity")]string userIdentity)

At least it works fine for ASP.NET Core 2.1 and Swashbuckle.AspNetCore 2.5.0.



回答5:

For those who use NSwag and need a custom header:

app.UseSwaggerUi3(typeof(Startup).GetTypeInfo().Assembly, settings =>
      {
          settings.GeneratorSettings.IsAspNetCore = true;
          settings.GeneratorSettings.OperationProcessors.Add(new OperationSecurityScopeProcessor("custom-auth"));

          settings.GeneratorSettings.DocumentProcessors.Add(
              new SecurityDefinitionAppender("custom-auth", new SwaggerSecurityScheme
                {
                    Type = SwaggerSecuritySchemeType.ApiKey,
                    Name = "header-name",
                    Description = "header description",
                    In = SwaggerSecurityApiKeyLocation.Header
                }));
        });            
    }

Swagger UI will then include an Authorize button.



回答6:

Here's a simpler answer for the ASP.NET Core Web Api/Swashbuckle combo, that doesn't require you to register any custom filters. Third time's a charm you know :).

Adding the code below to your Swagger config will cause the Authorize button to appear, allowing you to enter a bearer token to be sent for all requests. Don't forget to enter this token as Bearer <your token here> when asked.

Note that the code below will send the token for any and all requests and operations, which may or may not be what you want.

services.AddSwaggerGen(c =>
{
    //...

    c.AddSecurityDefinition("Bearer", new ApiKeyScheme()
    {
        Description = "JWT Authorization header using the Bearer scheme. Example: \"Authorization: Bearer {token}\"",
        Name = "Authorization",
        In = "header",
        Type = "apiKey"
    });

    c.AddSecurityRequirement(new Dictionary<string, IEnumerable<string>>
    {
        { "Bearer", new string[] { } }
    });

    //...
}


Via this thread.



回答7:

I ended up here because I was trying to conditionally add header parameters in Swagger UI, based on my own [Authentication] attribute I added to my API method. Following the hint that @Corcus listed in a comment, I was able to derive my solution, and hopefully it will help others.

Using Reflection, it's checking if the method nested down in apiDescription has the desired attribute (MyApiKeyAuthenticationAttribute, in my case). If it does, I can append my desired header parameters.

public void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription) {
    if (operation.parameters == null)
        operation.parameters = new List<Parameter>();


    var attributes = ((System.Web.Http.Controllers.ReflectedHttpActionDescriptor)
        ((apiDescription.ActionDescriptor).ActionBinding.ActionDescriptor)).MethodInfo
        .GetCustomAttributes(false);
    if(attributes != null && attributes.Any()) {
        if(attributes.Where(x => x.GetType() 
            == typeof(MyApiKeyAuthenticationAttribute)).Any()) {

            operation.parameters.Add(new Parameter {
                name = "MyApiKey",
                @in = "header",
                type = "string",
                description = "My API Key",
                required = true
            });
            operation.parameters.Add(new Parameter {
                name = "EID",
                @in = "header",
                type = "string",
                description = "Employee ID",
                required = true
            });
        }
    }


}


回答8:

DISCLAIMER: this solution is not using Header.

If someone is looking for a lazy-lazy manner (also in WebApi), I'd suggest:

public YourResult Authorize([FromBody]BasicAuthCredentials credentials)

You are not getting from header, but at least you have an easy alternative. You can always check the object for null and fallback to header mechanism.