Producing documentation is a laborious task that most developers dislike. Considering software delivery is an iterative approach, maintaining documentation becomes a full time work, especially for public APIs. For small teams and solo devs, tools like NSwag can do the heavy lifting of creating and maintaining documentation.

Benefits of NSwag:

  • Documentation based on XML code comments
  • Interactive web-based API client using SwaggerUI3
  • Ability to generate API client in a wide range of languages such as Typescript, C# and Java.

[toc]

Getting started

In this post, we will set up NSwag in a .NET Core web API.

The first step is to add the NSwag.AspNetCore package to your API project.

dotnet add package NSwag.AspNetCoreCode language: Bash (bash)

The package contains the middleware you need to add and use Swagger in Startup.cs.

Inside the ConfigureServices method, register Open API document generation.

services.AddOpenApiDocument(document =>
{
    document.Version = "v1";
    document.Title = Configuration["API:Name"];
    document.Description = Configuration["API:Description"];

    // For APIs that use JWT authentication
    document.AddSecurity("Bearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme
    {
        Type = OpenApiSecuritySchemeType.ApiKey,
        Name = "Authorization",
        In = OpenApiSecurityApiKeyLocation.Header,
        Description = "Type into the textbox: Bearer {your JWT token}."
    });
    document.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("Bearer"));
});Code language: C# (cs)

If your API does not use JWT authentication, you do not need to add security configuration above. Adding this configuration produces authentication controls on the Swagger UI web interface.

The next step is to use the Open API and SwaggerUI3. Again in Startup.cs, add the following code to before you map your controllers:

app.UseOpenApi();

app.UseSwaggerUi3(cfg =>
{
    cfg.DocumentTitle = "My Awesome API";
});

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllers();
});Code language: C# (cs)

That’s it! Swagger UI is now available on /swagger route.

You should see all of your API resources, operations and code comments you have been adding to controllers and objects exposed from your API.

Define SwaggerResponse

If you notice the accept header in SwaggerUI is not set to application/json.

This is be because Swagger does not know the return types of your endpoints. Setting SwaggerResponse on each controller action is a good habit to get into. After all, a tool is as good as you use it.

This is an example controller action:

[HttpGet]
[SwaggerResponse(typeof(IEnumerable&lt;BrandDto>))]
public async Task&lt;IActionResult> Get(string filter)
{
    var brands = await _brandRepository.GetAllAsync(filter);
    var dtos = _mapper.Map&lt;List&lt;BrandDto>>(brands);
    return Ok(dtos);
}Code language: C# (cs)

Hide a resource from SwaggerUI

For those “secret” resources that you do not want to show on the SwaggerUI, you can use the ApiExplorerSettings annotation.

[ApiController]
[Route("v1/[controller]")]
[ApiExplorerSettings(IgnoreApi = true)]
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
public class SecretController : ControllerBase
{}Code language: C# (cs)

This annotation also works on controller actions to hide individual endpoints.

Change swagger route

The default Swagger UI route is /swagger. I personally do not like it so I change it to /docs whenever I start a new API project.

app.UseSwaggerUi3(cfg =>
{
    cfg.Path = "/docs";
});Code language: C# (cs)

Rename swagger.json

In order to rename the default swagger.json specification file, you need to define your path in both the open api specification and the swagger UI.

The following example produces a specification file docs.json:

app.UseOpenApi(cfg =>
{
    cfg.Path = "/docs/{documentName}/doc.json"; ;
});

app.UseSwaggerUi3(cfg =>
{
    cfg.DocumentPath = "/docs/{documentName}/doc.json";
});Code language: C# (cs)

Expand resources

SwaggerUI allows us to control the way API resources are presented on the page. By default, DocExpansion property is set to none, meaning all resources and operations are collapsed.

I prefer displaying expanding resources and keeping operations collapsed, by setting DocExpansion to list. This achieves the look of Swagger Petstore.

app.UseSwaggerUi3(cfg =>
{
    cfg.DocExpansion = "list";
});Code language: C# (cs)

Preserve authentication header

SwaggerUI provides a way for users to authenticate with the API and save the token so that it is added to each request automatically.

While this is a great feature, the authentication token is wiped when the page is closed/refreshed.

Having researched online, I came across a pull request on the SwaggerUI GitHub repository. User @AmirBitaraf implemented a feature toggle the preserve authentication between page refreshes.

From what I can see online, this feature toggle has not been made available in NSwag. It is possible to add a key/value pair to the AdditionalSettings list available in UseSwaggerUi3 middleware.

app.UseSwaggerUi3(cfg =>
{
    cfg.AdditionalSettings.Add("persistAuthorization", true);
});Code language: C# (cs)

Customise object names

If you use DTO objects for exposing data in your API, you will find that SwaggerUI displays the class names as they are defined. In my opinion, including keyword DTO in a class name in the documentation just adds noise.

Fortunately, this can be changed with a custom schema name generator.

Inside Startup.cs, configure OpenApiDocument to accept a custom name generator for SwaggerUI.

services.AddOpenApiDocument(document =>
{
    document.SchemaNameGenerator = new NSwagNameGenerator();
});Code language: C# (cs)

NSwagNameGenerator overrides the Generate method to modify the class name.

internal class NSwagNameGenerator : DefaultSchemaNameGenerator, ISchemaNameGenerator
{
    public override string Generate(Type type)
    {
        return type.FullName.Replace("Dto", "");
    }
}Code language: C# (cs)

Inject custom JavaScript to SwaggerUI

If, for any reason you need to manipulate SwaggerUI HTML page, you can inject a JavaScript file. This would allow further modifications such as logo replacement, colour schemes etc.

app.UseSwaggerUi3(cfg =>
{
    cfg.CustomJavaScriptPath = "/docs/hello.js";
});Code language: C# (cs)

If you haven’t changed the default swagger route, your path would be /swagger/hello.js.

In order for the JavaScript file to be used correctly, you need to place it under wwwroot/docs/.

Also, enable serving static files in your API before using SwaggerUI middleware.

app.UseStaticFiles();Code language: C# (cs)

This approach also works for custom stylesheets, which is available with CustomStylesheetPath property.

Include XML documentation when building the project

Any code comment you add to your classes will appear on relevant operations within SwaggerUI interface. Usually, Visual Studio build places the XML documentation file inside the bin folder:

Project/bin/debug/.netcoreapp3.1/MyProject.xml

Swagger uses this file to populate its UI with your code comments. Since XML documentation is considered a dev artefact, this file is not included when the application is built remotely via dotnet build on Azure DevOps server.

We can include the XML documentation file by generating it on the project root and setting its Copy to output directory property to Alyways.

  1. Right-click on Project and select “Edit project file”. Alternatively open csproj file in a text editor.
  2. Set the DocumentationFile property to a desired file name. Ensure the file name appears on its own.
  3. Save and rebuild your project.
<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk.Web">
    <PropertyGroup>
        <TargetFramework>netcoreapp3.1</TargetFramework>
    </PropertyGroup>
    <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
     <DocumentationFile>MyProject.API.xml</DocumentationFile>
    </PropertyGroup>
...
</Project>Code language: HTML, XML (xml)

This forces Visual Studio to generate documentation XML on the root of the project. We can now include this file in the build output by adding the following to the csproj file:

<ItemGroup>
  <None Update="MyProject.API.xml">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </None>
</ItemGroup>Code language: HTML, XML (xml)

You can alternatively use Visual Studio user interface to make the same change via file properties.

SwaggerUI will now show documentation when you publish your API.

Final thoughts

NSwag is an amazing tool that I use in all APIs that I work on. It saves a lot time when it comes to producing documentation and client code generation. Do you use NSwag or an alternative? I look forward to hearing your opinion in the comments!

Umut Esen

Software Engineer specialising in full-stack web application development.

Leave a Reply