- Overview
- Application Overview
- Integration of Swagger
- 1. Install Swagger NuGet Package
- 2. Register the SwaggerGen Service
- 3. Enable Swagger Middleware
- 4. Get Access to Swagger UI
- 5. Enhance Documentation with XML Comments
- 6. Apply Data Annotations for Validation
- 7. Secure Swagger Endpoints with API Key
- Conclusion
Overview
In this article, we will talk about the way to integrate Swagger into the API of the product catalog. This can be a great method to generate interactive API documentation that can be easily shared with developers.
Application Overview
We have a .NET Core 8 API application with endpoints for an e-commerce product catalog. It includes operations like listing, creating, updating, and deleting products in the inventory. A front-end client can interact with this API to display and manage product information effectively.
The core entity of this system is the Product class with a unique identity, various attributes, and its stock quantity.
123456789101112 | public class Product
{
public Guid Id { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
|
For data persistence and simplicity, Entity Framework (EF Core 8) is the ORM (Object Relational Mapper) and an in-memory database. The setup process involves:
- The installation of the EF Core in-memory database provider:
- The definition of the context class:
123456 | public class ProductCatalogContext : DbContext
{
public DbSet<Product> Products { get; set; }
public ProductCatalogContext(DbContextOptions<ProductCatalogContext> options) : base(options) { }
}
|
- The DbContext service registration:
1234 | var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ProductCatalogContext>(options =>
options.UseInMemoryDatabase("ProductCatalog")); |
We have chosen a simple in-memory database to keep the focus on the Swagger integration. Although an in-memory database is primarily used for testing, it will be easy to switch to another database like SQLite or SQL Server.
To interact with the database, we have a Repository class with some methods acting upon the corresponding data using the Product Catalog DbContext:
1234567891011121314151617181920212223242526272829303132333435363738394041 | public class ProductRepository : IProductRepository
{
private readonly ProductCatalogContext _context;
public ProductRepository(ProductCatalogContext context)
{
_context = context;
}
public async Task<IReadOnlyList<Product>> GetAllProductsAsync()
{
return await _context.Products.ToListAsync();
}
public async Task<Product> GetProductByIdAsync(Guid id)
{
return await _context.Products.FindAsync(id);
}
public async Task AddProductAsync(Product product)
{
_context.Products.Add(product);
await _context.SaveChangesAsync();
}
public async Task UpdateProductAsync(Product product)
{
_context.Entry(product).State = EntityState.Modified;
await _context.SaveChangesAsync();
}
public async Task DeleteProductAsync(Guid id)
{
var product = await _context.Products.FindAsync(id);
if (product != null)
{
_context.Products.Remove(product);
await _context.SaveChangesAsync();
}
}
}
|
In Program.cs, we can register the IProductRepository interface with its implementation ProductRepository as a scoped service:
Finally, we will have an API controller with action methods using the injected Repository to handle the product objects needed for each case.
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849 | [ApiController]
[Route("api/products")]
public class ProductsController : ControllerBase
{
private readonly IProductRepository _repository;
public ProductsController(IProductRepository repository)
{
_repository = repository;
}
[HttpGet]
public async Task<IReadOnlyList<Product>> GetProducts()
{
return await _repository.GetAllProductsAsync();
}
[HttpGet("{id}")]
public async Task<Product> GetProduct(Guid id)
{
return await _repository.GetProductByIdAsync(id);
}
[HttpPost]
public async Task<Product> CreateProduct(Product product)
{
await _repository.AddProductAsync(product);
return product;
}
[HttpPut]
public async Task<Product> UpdateProduct(Product updatedProduct)
{
await _repository.UpdateProductAsync(updatedProduct);
return updatedProduct;
}
[HttpDelete("{id}")]
public async Task<bool> DeleteProduct(Guid id)
{
var product = await _repository.GetProductByIdAsync(id);
await _repository.DeleteProductAsync(id);
return true;
}
}
|
Integration of Swagger
The following section covers the integration of Swagger in an existing .Net Core API, specifically the product catalog API. We will start with the basic setup at the beginning and then will have a look at some custom features from Swagger that can be added.
1. Install Swagger NuGet Package
The Swashbuckle.AspNetCore package provides the tools to integrate Swagger into your .NET Core API. You need to navigate to the project directory and run the command:
2. Register the SwaggerGen Service
Then, you should add the SwaggerGen for the OpenApi specification to automatically generate API documentation for the product catalog app. Further, create a document with a meaningful name, title, and version instead of using the default settings.
12345678 | builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc(name: "v1", new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = "Product Catalog",
Version = "v1"
});
});
|
The name and the version must be the same when we set a SwaggerDoc. Swagger will organize and display the documentation based on this, ensuring that we can see the correct API version.
3. Enable Swagger Middleware
We will enable both the Swagger JSON endpoint and the Swagger UI, configuring Swagger Middlewares. We can set the RoutePrefix to an empty string, as it makes the Swagger UI accessible from the root URL.
1234567 | app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.RoutePrefix = string.Empty;
options.SwaggerEndpoint("/swagger/v1/swagger.json", "Product Catalog API V1");
});
|
4. Get Access to Swagger UI
Everything is ready now. To view the Swagger UI, we can navigate to https://localhost:<port>:
The swagger.json endpoint listens to http://localhost:<port>/swagger/v1/swagger.json:
5. Enhance Documentation with XML Comments
Some action methods may need further explanation. Triple-slash comments to the section header of each method will enhance the Swagger UI and the implementation will be clearer to the consumer. To enable XML Comments:
- Right Click on the API Project in Visual Studio and click Properties
- In Build Tab -> Output -> check the Documentation file.
- In Build Tab -> Errors and Warnings -> Suppress 1591 that gives warnings to any method, class, or field that doesn’t have triple-slash comments.
- Include the generated XML documentation file in Swagger:
123456789101112 | builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc(name: "v1", new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = "Product Catalog",
Version = "v1"
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
|
- Add a summary to describe the GetProducts method to see what will be displayed on the Swagger UI:
123456789 | /// <summary>
/// Retrieves the list of all products.
/// </summary>
/// <returns>A readonly list of products.</returns>
[HttpGet]
public async Task<IReadOnlyList<Product>> GetProducts()
{
return await _repository.GetAllProductsAsync();
} |
As you can see, this summary will appear next to the HttpGet action method, providing users with a clear description of its functionality.
6. Apply Data Annotations for Validation
To ensure proper validation in our .NET Core application, we can use data annotations. Let’s make the name property required. The Swagger UI will identify this change and mark the name as mandatory.
Add the [Required] attribute to the Name property of the Product Class:
12345678910111213 | public class Product
{
public Guid Id { get; set; }
[Required]
public string Name { get; set; }
public string Description { get; set; }
public decimal Price { get; set; }
public int Stock { get; set; }
}
|
Check the Product Schema to see the Swagger UI change:
A red asterisk appeared next to the name. It indicates that it is required to add the name.
7. Secure Swagger Endpoints with API Key
The product catalog API is accessible to anyone with this implementation so far. But we need to provide access only to those clients that have the necessary rights. OpenAPI supports various security schemes to do this. We will use an API key to authenticate requests.
To enable the api-key mechanism, we will create and register a custom middleware. This will extract the X-API-KEY header from incoming requests and compare it against the key defined in appsettings.json. Let’s start:
- An API key will be stored in appsetting.json:
- The implementation of the API key middleware can be performed the following way:
1234567891011121314151617181920212223 | public class ApiKeyMiddleware
{
private readonly RequestDelegate _next;
private const string APIKEY_HEADER = "X-API-KEY";
public ApiKeyMiddleware(RequestDelegate next)
{
_next = next;
}
public async Task InvokeAsync(HttpContext context, IConfiguration configuration)
{
if (!context.Request.Headers.TryGetValue(APIKEY_HEADER, out var extractedApiKey) ||
!configuration.GetValue<string>("ApiKey").Equals(extractedApiKey))
{
context.Response.StatusCode = 401;
await context.Response.WriteAsync("Unauthorized client.");
return;
}
await _next(context);
}
}
|
- Now, let’s add the custom middleware to the request pipeline on program.cs:
Next, we need to integrate this security mechanism into Swagger. Let’s add SecurityDefinition and SecurityRequirement:
12345678910111213141516171819202122232425262728293031323334353637 | builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc(name: "v1", new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = "Product Catalog",
Version = "v1"
});
options.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Name = "X-API-KEY",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "ApiKeyScheme"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey"
},
Scheme = "ApiKeyScheme",
Name = "X-API-KEY",
In = ParameterLocation.Header
},
new List<string>()
}
});
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
options.IncludeXmlComments(xmlPath);
});
|
After that Swagger will add an Authorize button on the top right corner of its UI. The user can enter a valid API key to be sent with future requests.
By performing these steps, you can make sure that the API is secure. We will need to provide the valid api-key, using the authorize feature on Swagger to interact with our endpoints. Otherwise, it will trigger an Unauthorized client error.
Conclusion
Done! We have successfully integrated Swagger into the product catalog API. In this article, we walked through:
- Swagger Installation and Setup
- Documentation Generation
- XML Comments Integration
- Application of Data Annotations
- Security Configurations
By following the provided step-by-step instructions you can ensure smooth integration of Swagger in your own .NET Core software solution.
If you need any technical help or professional consultancy from our .NET experts, do not hesitate to contact us. Over the years of work in the software development industry, we have accumulated unique expertise in various business domains and will be able to cope with any type of tasks.