Wednesday, September 27, 2023

Best Practices when using Swagger with .NET Core

 Take your Swagger documentation to the next level by using several different configuration options:

  1. Replace your existing AddSwaggerGen and add information about your project.
  2. Pull your existing XML Documentation into Swagger
  3. Use Swagger Annotations to define return types and status codes.

Alter AddSwaggerGen

In your program.cs of your project, replace your AddSwagerGen with the following code, altering to the needs of your project.

var apiInfo = new OpenApiInfo
{
    Title = "Blogs API",
    Version = "v1",
    Description = "An API to perform Blog operations",
    TermsOfService = new Uri("https://example.com/terms"),
    Contact = new OpenApiContact
    {
        Name = "John Whorfin",
        Email = "John.Whorfin@gmail.com",
        Url = new Uri("https://twitter.com/jwhorfin"),
    },
    License = new OpenApiLicense
    {
        Name = "Blog API LICX",
        Url = new Uri("https://example.com/license"),
    }
};

var securityScheme = new OpenApiSecurityScheme
{
    Name = "Authorization",
    Type = SecuritySchemeType.Http,
    Scheme = "bearer",
    BearerFormat = "JWT",
    In = ParameterLocation.Header,
    Description = "JWT Authorization header using the Bearer scheme."
};

var securityRequirement = new OpenApiSecurityRequirement
{
    {
        new OpenApiSecurityScheme
        {
            Reference = new OpenApiReference
            {
                Type = ReferenceType.SecurityScheme,
                Id = "Bearer"
            }
        },
        Array.Empty<string>()
    }
};


// Register the Swagger generator, defining 1 or more Swagger documents
services.AddSwaggerGen(options =>
{
    options.SwaggerDoc("v1", apiInfo);
    options.AddSecurityDefinition("Bearer", securityScheme);
    options.AddSecurityRequirement(securityRequirement);
    
    // using System.Reflection;
    var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
    options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});


Generate XML Documentation

Open the properties file of your project.  Scroll down to the Build and then Output.  



Use Swagger Annotations on a Controller

Make sure you have a using Swashbuckle.AspNetCore.Annotations in your controller



using Swashbuckle.AspNetCore.Annotations;

[Route("api/[controller]")]
[ApiController]
public class CategoriesController : ControllerBase
{
    private readonly ICategoryRepository _categoryRepository;

    public CategoriesController(ICategoryRepository categoryRepository)
    {
        _categoryRepository = categoryRepository;
    }
    
    /// <summary>
    /// Get category by Id
    /// </summary>
    /// <remarks>Returns a single category</remarks>
    /// <param name="categoryId">Id of category to return</param>
    /// <param name="cancellationToken"></param>
    /// <response code="200">successful operation</response>
    /// <response code="401">Not authorized</response>
    /// <response code="404">Category not found</response>
    [HttpGet]
    [Consumes("application/json")]
    [Route("{categoryId:Guid}")]
    [SwaggerOperation("GetCategoryById")]
    [SwaggerResponse(statusCode: 200, type: typeof(Category), description: "Successful operation")]
    [SwaggerResponse(statusCode: 404, type: typeof(ApiError), description: "Category not found")]
    [ProducesResponseType(typeof(GetCategoryResponse), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> GetCategoryById([FromRoute] Guid categoryId, CancellationToken cancellationToken)
    {
        //Implementation
    }

    /// <summary>
    /// Get all categories
    /// </summary>
    /// <param name="cancellationToken"></param>
    /// <remarks>Returns all categories</remarks>
    /// <response code="200">Successful Operation</response>
    /// <response code="401">Not authorized</response>
    [HttpGet]
    [Consumes("application/json")]
    [Route("/Categories/GetAllCategories")]
    [SwaggerOperation("GetAllCategories")]
    [SwaggerResponse(statusCode: 200, type: typeof(Category), description: "Successful Operation")]
    [ProducesResponseType(typeof(List<Category>), StatusCodes.Status200OK)]
    public async Task<IActionResult> GetAllCategories(CancellationToken cancellationToken)
    {
        //Implementation
    }

    /// <summary>
    /// Add a new category
    /// </summary>
    /// <remarks>Adds a new category</remarks>
    /// <param name="body">Category object that needs to be added</param>
    /// <param name="cancellationToken"></param>
    /// <response code="201">Category created</response>
    /// <response code="400">Invalid input</response>
    /// <response code="401">Not authorized</response>
    [HttpPost]
    [Consumes("application/json")]
    [SwaggerOperation("AddCategory")]
    [SwaggerResponse(statusCode: 201, type: typeof(CategoryRequest), description: "Category created")]
    [SwaggerResponse(statusCode: 400, type: typeof(ApiError), description: "Invalid input")]
    [ProducesResponseType(typeof(Category), StatusCodes.Status201Created)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status400BadRequest)]
    public async Task<IActionResult> AddCategory([FromBody] CategoryRequest body, CancellationToken cancellationToken)
    {
        //Implementation
    }

    /// <summary>
    /// Update category
    /// </summary>
    /// <remarks>Updates category</remarks>
    /// <param name="body">Category object that needs to be updated</param>
    /// <param name="categoryId"></param>
    /// <param name="cancellationToken"></param>
    /// <response code="200">successful operation</response>
    /// <response code="401">Not authorized</response>
    /// <response code="404">Category not found</response>
    [HttpPut]
    [Route("{categoryId:guid}")]
    [Consumes("application/json")]
    [SwaggerOperation("UpdateCategory")]
    [SwaggerResponse(statusCode: 200, type: typeof(Category), description: "Successful Operation")]
    [SwaggerResponse(statusCode: 404, type: typeof(ApiError), description: "Category not found")]
    [ProducesResponseType(typeof(Category), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> UpdateCategory([FromBody] CategoryRequest body, [FromRoute] Guid categoryId, CancellationToken cancellationToken)
    {
        //Implementation
    }    


    /// <summary>
    /// Deletes a category by Id
    /// </summary>
    /// <param name="categoryId">Category id to delete</param>
    /// <param name="cancellationToken"></param>
    /// <response code="200">successful operation</response>
    /// <response code="401">Not authorized</response>
    /// <response code="404">Category not found</response>
    [HttpDelete]
    [Consumes("application/json")]
    [Route("{categoryId:Guid}")]
    [SwaggerOperation("DeleteCategory")]
    [SwaggerResponse(statusCode: 200, type: typeof(Category), description: "Successful Operation")]
    [SwaggerResponse(statusCode: 404, type: typeof(ApiError), description: "Category not found")]
    [ProducesResponseType(typeof(bool), StatusCodes.Status200OK)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status400BadRequest)]
    [ProducesResponseType(typeof(ApiError), StatusCodes.Status404NotFound)]
    public async Task<IActionResult> DeleteCategory(Guid categoryId, CancellationToken cancellationToken)
    {
        //Implementation
    }
}

References:



No comments:

Post a Comment