Skip to content

2 Troubleshooting

Helder Sepulveda edited this page Jun 4, 2017 · 3 revisions

Troubleshooting??? I thought this was all supposed to be "seamless"? OK you've called me out! Things shouldn't go wrong, but if they do, take a look at the FAQs for inspiration.

Customizing the Generated Swagger Docs

The following snippet demonstrates the minimum configuration required to get the Swagger docs and swagger-ui up and running:

httpConfiguration
      .EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API"))
      .EnableSwaggerUi();

These methods expose a range of configuration and extensibility options that you can pick and choose from, combining the convenience of sensible defaults with the flexibility to customize where you see fit. Read on to learn more.

Custom Routes

The default route templates for the Swagger docs and swagger-ui are "swagger/docs/{apiVersion}" and "swagger/ui/{*assetPath}" respectively. You're free to change these so long as the provided templates include the relevant route parameters - {apiVersion} and {*assetPath}.

httpConfiguration
    .EnableSwagger("docs/{apiVersion}/swagger", c => c.SingleApiVersion("v1", "A title for your API"))
    .EnableSwaggerUi("sandbox/{*assetPath}");

In this case the URL to swagger-ui will be sandbox/index.

Pretty Print

If you want the output Swagger docs to be indented properly, enable the PrettyPrint option as following:

httpConfiguration
    .EnableSwagger(c => c.PrettyPrint())
    .EnableSwaggerUi();

Additional Service Metadata

In addition to operation descriptions, Swagger 2.0 includes several properties to describe the service itself. These can all be provided through the configuration API:

httpConfiguration
    .EnableSwagger(c =>
        {
            c.RootUrl(req => GetRootUrlFromAppConfig());

            c.Schemes(new[] { "http", "https" });

            c.SingleApiVersion("v1", "Swashbuckle.Dummy")
                .Description("A sample API for testing and prototyping Swashbuckle features")
                .TermsOfService("Some terms")
                .Contact(cc => cc
                    .Name("Some contact")
                    .Url("http://tempuri.org/contact")
                    .Email("some.contact@tempuri.org"))
                .License(lc => lc
                    .Name("Some License")
                    .Url("http://tempuri.org/license"));
        });

RootUrl

By default, the service root url is inferred from the request used to access the docs. However, there may be situations (e.g. proxy and load-balanced environments) where this does not resolve correctly. You can workaround this by providing your own code to determine the root URL.

Schemes

If schemes are not explicitly provided in a Swagger 2.0 document, then the scheme used to access the docs is taken as the default. If your API supports multiple schemes and you want to be explicit about them, you can use the Schemes option.

SingleApiVersion

Use this to describe a single version API. Swagger 2.0 includes an "Info" object to hold additional metadata for an API. Version and title are required but you may also provide additional fields as shown above.

NOTE: If your Web API is hosted in IIS, you should avoid using full-stops in the version name (e.g. "1.0"). The full-stop at the tail of the URL will cause IIS to treat it as a static file (i.e. with an extension) and bypass the URL Routing Module and therefore, Web API.

Describing Multiple API Versions

If your API has multiple versions, use MultipleApiVersions instead of SingleApiVersion. In this case, you provide a lambda that tells Swashbuckle which actions should be included in the docs for a given API version. Like SingleApiVersion, Version also returns an "Info" builder so you can provide additional metadata per API version.

httpConfiguration
    .EnableSwagger(c =>
        {
            c.MultipleApiVersions(
                (apiDesc, targetApiVersion) => ResolveVersionSupportByRouteConstraint(apiDesc, targetApiVersion),
                (vc) =>
                {
                    vc.Version("v2", "Swashbuckle Dummy API V2");
                    vc.Version("v1", "Swashbuckle Dummy API V1");
                });
        });
    .EnableSwaggerUi(c =>
        {
            c.EnableDiscoveryUrlSelector();
        });

* You can also enable a select box in the swagger-ui (as shown above) that displays a discovery URL for each version. This provides a convenient way for users to browse documentation for different API versions.

Describing Security/Authorization Schemes

You can use BasicAuth, ApiKey or OAuth2 options to describe security schemes for the API. See https://github.com/swagger-api/swagger-spec/blob/master/versions/2.0.md for more details.

httpConfiguration
     .EnableSwagger(c =>
         {
             //c.BasicAuth("basic")
             //    .Description("Basic HTTP Authentication");

             //c.ApiKey("apiKey")
             //    .Description("API Key Authentication")
             //    .Name("apiKey")
             //    .In("header");

             c.OAuth2("oauth2")
                 .Description("OAuth2 Implicit Grant")
                 .Flow("implicit")
                 .AuthorizationUrl("http://petstore.swagger.wordnik.com/api/oauth/dialog")
                 //.TokenUrl("https://tempuri.org/token")
                 .Scopes(scopes =>
                 {
                     scopes.Add("read", "Read access to protected resources");
                     scopes.Add("write", "Write access to protected resources");
                 });

             c.OperationFilter<AssignOAuth2SecurityRequirements>();
         });
     .EnableSwaggerUi(c =>
         {
             c.EnableOAuth2Support("test-client-id", "test-realm", "Swagger UI");
         });

NOTE: These only define the schemes and need to be coupled with a corresponding "security" property at the document or operation level to indicate which schemes are required for each operation. To do this, you'll need to implement a custom IDocumentFilter and/or IOperationFilter to set these properties according to your specific authorization implementation

* If your API supports the OAuth2 Implicit flow, and you've described it correctly, according to the Swagger 2.0 specification, you can enable UI support as shown above.

Customize the Operation Listing

If necessary, you can ignore obsolete actions and provide custom grouping/sorting strategies for the list of Operations in a Swagger document:

httpConfiguration
    .EnableSwagger(c =>
        {
            c.IgnoreObsoleteActions();

            c.GroupActionsBy(apiDesc => apiDesc.HttpMethod.ToString());

            c.OrderActionGroupsBy(new DescendingAlphabeticComparer());
        });

IgnoreObsoleteActions

Set this flag to omit operation descriptions for any actions decorated with the Obsolete attribute

NOTE: If you want to omit specific operations but without using the Obsolete attribute, you can create an IDocumentFilter or make use of the built in ApiExplorerSettingsAttribute

GroupActionsBy

Each operation can be assigned one or more tags which are then used by consumers for various reasons. For example, the swagger-ui groups operations according to the first tag of each operation. By default, this will be the controller name but you can use this method to override with any value.

OrderActionGroupsBy

You can also specify a custom sort order for groups (as defined by GroupActionsBy) to dictate the order in which operations are listed. For example, if the default grouping is in place (controller name) and you specify a descending alphabetic sort order, then actions from a ProductsController will be listed before those from a CustomersController. This is typically used to customize the order of groupings in the swagger-ui.

Modifying Generated Schemas

Swashbuckle makes a best attempt at generating Swagger compliant JSON schemas for the various types exposed in your API. However, there may be occasions when more control of the output is needed. This is supported through the following options:

httpConfiguration
      .EnableSwagger(c =>
          {
              c.MapType<ProductType>(() => new Schema { type = "integer", format = "int32" });

              c.SchemaFilter<ApplySchemaVendorExtensions>();

              //c.UseFullTypeNameInSchemaIds();

              c.SchemaId(t => t.FullName.Contains('`') ? t.FullName.Substring(0, t.FullName.IndexOf('`')) : t.FullName);
              
              c.IgnoreObsoleteProperties();

              c.DescribeAllEnumsAsStrings();
          });

MapType

Use this option to override the Schema generation for a specific type.

It should be noted that the resulting Schema will be placed "inline" for any applicable Operations. While Swagger 2.0 supports inline definitions for "all" Schema types, the swagger-ui tool does not. It expects "complex" Schemas to be defined separately and referenced. For this reason, you should only use the MapType option when the resulting Schema is a primitive or array type.

If you need to alter a complex Schema, use a Schema filter.

SchemaFilter

If you want to post-modify "complex" Schemas once they've been generated, across the board or for a specific type, you can wire up one or more Schema filters.

ISchemaFilter has the following interface:

void Apply(Schema schema, SchemaRegistry schemaRegistry, Type type);

A typical implementation will inspect the system Type and modify the Schema accordingly. If necessary, the schemaRegistry can be used to obtain or register Schemas for other Types

UseFullTypeNamesInSchemaIds

In a Swagger 2.0 document, complex types are typically declared globally and referenced by unique Schema Id. By default, Swashbuckle does NOT use the full type name in Schema Ids. In most cases, this works well because it prevents the "implementation detail" of type namespaces from leaking into your Swagger docs and UI. However, if you have multiple types in your API with the same class name, you'll need to opt out of this behavior to avoid Schema Id conflicts.

SchemaId

Use this option to provide your own custom strategy for inferring SchemaId's for describing "complex" types in your API.

IgnoreObsoleteProperties

Set this flag to omit schema property descriptions for any type properties decorated with the Obsolete attribute

DescribeAllEnumsAsStrings

In accordance with the built in JsonSerializer, Swashbuckle will, by default, describe enums as integers. You can change the serializer behavior by configuring the StringToEnumConverter globally or for a given enum type. Swashbuckle will honor this change out-of-the-box. However, if you use a different approach to serialize enums as strings, you can also force Swashbuckle to describe them as strings.

Modifying Generated Operations

Similar to Schema filters, Swashbuckle also supports Operation and Document filters:

httpConfiguration
     .EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API"))
         {
             c.OperationFilter<AddDefaultResponse>();

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

OperationFilter

Post-modify Operation descriptions once they've been generated by wiring up one or more Operation filters.

IOperationFilter has the following interface:

void Apply(Operation operation, SchemaRegistry schemaRegistry, ApiDescription apiDescription);

A typical implementation will inspect the ApiDescription and modify the Operation accordingly. If necessary, the schemaRegistry can be used to obtain or register Schemas for Types that are used in the Operation.

DocumentFilter

Post-modify the entire Swagger document by wiring up one or more Document filters.

IDocumentFilter has the following interface:

void Apply(SwaggerDocument swaggerDoc, SchemaRegistry schemaRegistry, IApiExplorer apiExplorer);

This gives full control to modify the final SwaggerDocument. You can gain additional context from the provided SwaggerDocument (e.g. version) and IApiExplorer. You should have a good understanding of the Swagger 2.0 spec. before using this option.

Wrapping the SwaggerGenerator with Additional Behavior

The default implementation of ISwaggerProvider, the interface used to obtain Swagger metadata for a given API, is the SwaggerGenerator. If neccessary, you can inject your own implementation or wrap the existing one with additional behavior. For example, you could use this option to inject a "Caching Proxy" that attempts to retrieve the SwaggerDocument from a cache before delegating to the built-in generator:

httpConfiguration
      .EnableSwagger(c => c.SingleApiVersion("v1", "A title for your API"))
          {
        c.CustomProvider((defaultProvider) => new CachingSwaggerProvider(defaultProvider));
          });

Including XML Comments

If you annotate Controllers and API Types with Xml Comments, you can incorporate those comments into the generated docs and UI. The Xml tags are mapped to Swagger properties as follows:

  • Action summary -> Operation.summary
  • Action remarks -> Operation.description
  • Parameter summary -> Parameter.description
  • Type summary -> Schema.descripton
  • Property summary -> Schema.description (i.e. on a property Schema)

You can enable this by providing the path to one or more XML comments files:

httpConfiguration
    .EnableSwagger(c =>
        {
            c.SingleApiVersion("v1", "A title for your API");
            c.IncludeXmlComments(GetXmlCommentsPathForControllers());
            c.IncludeXmlComments(GetXmlCommentsPathForModels());
        });

NOTE: You will need to enable output of the XML documentation file. This is enabled by going to project properties -> Build -> Output. The "XML documentation file" needs to be checked and a path assigned, such as "bin\Debug\MyProj.XML". You will also want to verify this across each build configuration. Here's an example of reading the file, but it may need to be modified according to your specific project settings:

httpConfiguration
    .EnableSwagger(c =>
        {
            var baseDirectory = AppDomain.CurrentDomain.BaseDirectory;
            var commentsFileName = Assembly.GetExecutingAssembly().GetName().Name + ".XML";
            var commentsFile = Path.Combine(baseDirectory, commentsFileName);

            c.SingleApiVersion("v1", "A title for your API");
            c.IncludeXmlComments(commentsFile);
            c.IncludeXmlComments(GetXmlCommentsPathForModels());
        });

Response Codes

Swashbuckle will automatically create a "success" response for each operation based on the action's return type. If it's a void, the status code will be 204 (No content), otherwise 200 (Ok). This mirrors WebApi's default behavior. If you need to change this and/or list additional response codes, you can use the non-standard "response" tag:

/// <response code="201">Account created</response>
/// <response code="400">Username already in use</response>
public int Create(Account account)

Working Around Swagger 2.0 Constraints

In contrast to Web API, Swagger 2.0 does not include the query string component when mapping a URL to an action. As a result, Swashbuckle will raise an exception if it encounters multiple actions with the same path (sans query string) and HTTP method. You can workaround this by providing a custom strategy to pick a winner or merge the descriptions for the purposes of the Swagger docs

httpConfiguration
    .EnableSwagger((c) =>
        {
            c.SingleApiVersion("v1", "A title for your API"));
            c.ResolveConflictingActions(apiDescriptions => apiDescriptions.First());
        });

See the following discussion for more details:

https://github.com/domaindrivendev/Swashbuckle/issues/142