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.
Table of contents
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.AspNetCore
Code 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<BrandDto>))]
public async Task<IActionResult> Get(string filter)
{
var brands = await _brandRepository.GetAllAsync(filter);
var dtos = _mapper.Map<List<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
.
- Right-click on Project and select “Edit project file”. Alternatively open
csproj
file in a text editor. - Set the
DocumentationFile
property to a desired file name. Ensure the file name appears on its own. - 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!