.Net Core 3.0 API and Swashbuckle: Disabled Model Binding and XML Document File

Written by gacovak | Published 2019/11/20
Tech Story Tags: swashbuckle | .netcore | api | swagger | latest-tech-stories | api-testing | rest-api

TLDR Swashbuckle is a handy tool which you can use to document your API. It's very powerful and in my opinion, that implies that it's quite complex and can be a bit overwhelming. I used it recently on a project and found myself spending more than a few hours of searching the internet to find the answers to all my questions. The easiest way to do that is by adding comments in the files. I'm using 5.0.0-rc2.0 and I had disabled model binding an endpoint and I needed a way to describe the schema of the request that was expecting. That was expecting that I was expecting the request.via the TL;DR App

Swashbuckle is a handy tool which you can use to document your API. It's very powerful and in my opinion, that implies that it's quite complex and can be a bit overwhelming. I used it recently on a project and found myself spending more than a few hours of searching the internet to find the answers to all my questions. Considering the time it took me to put everything together, I decided it was worth to share my findings.
You can find the sample code here: https://github.com/kgacova/swashbuckle-sandbox
So after you create your API project, you need to install
Swashbuckle.AspNetCore
. I'm using 5.0.0-rc2. Then you need to register it in the Startup.cs. I'm doing a basic setup here for brevity sake, but feel free to explore the configuration options available.
public void ConfigureServices(IServiceCollection services)
        {
            services.AddSwaggerGen(options =>
            {
                options.SwaggerDoc("v1", new OpenApiInfo()
                {
                    Description="Well documented api",
                    Title = "Sandbox Api"
                });
            });
            //other stuff
        }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
                  app.UseSwagger();

            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Sandbox Api");
            });
//other stuff...
        }
    
Now start your project and navigate to
/swagger/index.html
, you should be able to view your endpoints.
For me it looks like this:
As you can see, there isn't much explanation here, at least not from a domain perspective, it's mostly technical information. Lets fix that. I found the easiest way to do that is by adding comments in the files. You might argue this, but imo the closer the docs are to the code, the easier it is to keep them up-to-date, hence relevant. So that being said, first thing you need to do is enable xml documentation for your project. Go to the solution explorer, right click on the project -> properties and then under build -> tick the XML documentation file and hit ctrl+s. And don't forget to set the xml file to copy to output directory.
As you can see, I have two projects in my solution because I decided to put my models in a separate library project. I will also need to document these classes and if this is your use case too, then you mustn't forget to enable xml documentations for those projects too. Next, instruct swashbuckle to use those files. I'm adding the following two lines to my ConfigureServices method in the AddSwaggerGen options:
options.IncludeXmlComments(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase) + "\\SandboxApi.xml", true);
options.IncludeXmlComments(Path.GetDirectoryName(Assembly.GetExecutingAssembly().CodeBase) + "\\Models.xml");
Now, for the controllers you can write something like:
/// <summary>
/// A controller for CRUD operations with weather forecast
/// </summary>
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[]
        {
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
        };
...
Same for the models:
    /// <summary>
    /// Detailed weather forecast
    /// </summary>
    public class WeatherForecast
    {
        /// <summary>
        /// The date for the forecast
        /// </summary>
        public DateTime Date { get; set; }
Usage of
<remarks>
,
<returns>
and
<example>
is also supported. You can see the explanations showing up for the endpoints and you can explore the schemas at the bottom of the swagger index page.
That's pretty easy and straight forward. But in my use case I had disabled model binding for an endpoint and I needed a way to describe the schema of the request that endpoint was expecting. That's where IOperationFilter comes into play. Create your custom filter which implements the interface.
public class WeatherPostOperationFilter : IOperationFilter
    {
        public void Apply(OpenApiOperation operation, OperationFilterContext context)
        {
            if (operation.OperationId != "Weather_Post")
                return;
            operation.RequestBody = new OpenApiRequestBody()
            {
                Content = new Dictionary<string, OpenApiMediaType> {
                    {"multipart/form-data",
                    new OpenApiMediaType()
                    {
                        Schema = context.SchemaGenerator
                        .GenerateSchema(typeof(WeatherForecast), context.SchemaRepository)
                    }
                    }
                }
            };
        }
    }
In it, specify the schema and add it to the schema repository. You can notice that there's an if statement that checks the operation id. If you remove those two lines, this filter will be applied to all endpoints. Since we don't want that in this case, we add the condition and specify the operation id on the targeted endpoint like so:
[HttpPost(Name ="Weather_Post")]
public IEnumerable<WeatherForecast> Post()
        {
//...
Then register the filter in the services.AddSwaggerGen call in the ConfigureServices method:
options.OperationFilter<WeatherPostOperationFilter>();
And you're all set.
As I said in the beginning, Swashbuckle is a very powerful tool and there's plenty you can do with it. I've shown here only two scenarios, but these two approaches were enough to describe my small API in great detail and communicate to my consumers a lot about the domain. Hope this example will help you get on your way with Swashbuckle. Happy coding!

Published by HackerNoon on 2019/11/20