Saturday, January 22, 2022

A clean way to add Swagger to ASP.NET Core application

 Swagger needs no introduction. It is one of the best methods to test your WEB APIs. Adding swagger to your ASP.NET Core application is very easy and straightforward. I already have a couple of posts on my blog on Swagger. Swagger comes with many options and customization to help you prepare better API documentation. The ASP.NET Core Startup.cs is the place to add Swagger or any middleware that you would like to use in your ASP.NET Core application. The Startup.cs file will become lengthy when we use more swagger customization. So in this post, we’ll see a clean way to add Swagger to ASP.NET Core application using C# extension methods.

A clean way to add Swagger to ASP.NET Core application

Just to refresh your memory, you need to install Swashbuckle.AspNetCore nuget package which comprises of – a Swagger generator, middleware to expose the generated Swagger as JSON endpoints and middleware to expose a swagger-ui that’s powered by those endpoints. Once the package is installed, the minimum code required to configure swagger in the Startup.cs would be:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
       c.SwaggerDoc("v1", new OpenApiInfo { Title = "My API", Version = "v1" });
    });
}
 
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
    });
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

Here, the code to configure swagger is very minimal. But in real projects, it will be more than this. Here, look at the sample Startup.cs file from one of my ASP.NET Core application. This swagger code can grow if more customization is done.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers();
    services.AddSwaggerGen(c =>
    {
        c.SwaggerDoc("v1", new OpenApiInfo
        {
            Version = "v1",
            Title = "My API",
            Description = "My First ASP.NET Core Web API",
            TermsOfService = new System.Uri("www.talkingdotnet.com"),
            Contact = new OpenApiContact() { Name = "Talking Dotnet", Email = "contact@talkingdotnet.com" }
        });
 
        c.SwaggerDoc("v2", new OpenApiInfo
        {
            Version = "v2",
            Title = "New API V2",
            Description = "Sample Web API",
            TermsOfService = new System.Uri("www.talkingdotnet.com"),
            Contact = new OpenApiContact() { Name = "Talking Dotnet", Email = "contact@talkingdotnet.com" }
        });
 
        c.DescribeAllEnumsAsStrings();
        c.DescribeStringEnumsInCamelCase();
    });
}
 
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }
    app.UseHttpsRedirection();
    app.UseRouting();
    app.UseAuthorization();
    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
        c.SwaggerEndpoint("/swagger/v2/swagger.json", "My API V2");
    });
    app.UseEndpoints(endpoints =>
    {
        endpoints.MapControllers();
    });
}

ASP.NET Core startup file configures the request pipeline, contains all the middleware, services, policies and options to configure. In large applications, this could result in hundreds of lines of code. But, there is a way to clean this mess and make Startup.cs more readable and manageable.

We can leverage C# extension methods to create extension methods for IApplicationBuilder or IServiceCollection. An extension method is actually a special kind of static method defined in a static class. To define an extension method,

  • First, create a static class.
  • Inside the class, define a static method as an extension method where the first parameter of the extension method specifies the type on which the extension method is applicable.
  • Here is our SwaggerExtension class.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    public static class SwaggerExtension
    {
        public static void AddSwagger(this IServiceCollection services)
        {
            services.AddSwaggerGen(c =>
            {
                c.SwaggerDoc("v1", new OpenApiInfo
                {
                    Version = "v1",
                    Title = "My API",
                    Description = "My First ASP.NET Core Web API",
                    TermsOfService = new System.Uri("https://www.talkingdotnet.com"),
                    Contact = new OpenApiContact() { Name = "Talking Dotnet", Email = "contact@talkingdotnet.com" }
                });
     
                c.SwaggerDoc("v2", new OpenApiInfo
                {
                    Version = "v2",
                    Title = "New API V2",
                    Description = "Sample Web API",
                    TermsOfService = new System.Uri("https://www.talkingdotnet.com"),
                    Contact = new OpenApiContact() { Name = "Talking Dotnet", Email = "contact@talkingdotnet.com" }
                });
     
                c.DescribeAllEnumsAsStrings();
                c.DescribeStringEnumsInCamelCase();
            });
        }
        public static void UseCustomSwagger(this IApplicationBuilder app)
        {
            app.UseSwagger();
            app.UseSwaggerUI(c =>
            {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
                c.SwaggerEndpoint("/swagger/v2/swagger.json", "My API V2");
            });
        }
    }

    There are 2 extension methods defined respectively for IServiceCollection and IApplicationBuilder. You can now call the extension methods in the Startup.cs class. Like,

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddControllers();
        services.AddSwagger();
    }
     
    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        app.UseHttpsRedirection();
        app.UseRouting();
        app.UseAuthorization();
        app.UseCustomSwagger();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

    That’s it.

    Summary

    To summarize, using extension methods, we can make the Startup.cs clean and readable. We should create extension methods for each service added to the IServiceCollection and IApplicationBuilder in separate files. This will help in code reusability and also make code manageable.

    Thank you for reading. Keep visiting this blog and share this in your network. Please put your thoughts and feedback in the comments section.

    No comments:

    Post a Comment

    How to register multiple implementations of the same interface in Asp.Net Core?

     Problem: I have services that are derived from the same interface. public interface IService { } public class ServiceA : IService { ...