Configuring Controllers for OpenAPI
Start and run your project, and you'll notice that the OpenAPI docs are a little more lonely now because we removed our minimal APIs, which had all of the nice descriptions and tags and stuff.
Let's update our endpoints to have some descriptions and tags.
public class EmployeesController : BaseController
{
private readonly IRepository<Employee> _repository;
private readonly ILogger<EmployeesController> _logger;
public EmployeesController(IRepository<Employee> repository, ILogger<EmployeesController> logger)
{
_repository = repository;
_logger = logger;
}
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<GetEmployeeResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult GetAllEmployees()
{
}
[HttpGet("{id:int}")]
[ProducesResponseType(typeof(GetEmployeeResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult GetEmployeeById(int id)
{
}
[HttpPost]
[ProducesResponseType(typeof(GetEmployeeResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateEmployee([FromBody] CreateEmployeeRequest employeeRequest)
{
}
[HttpPut("{id}")]
[ProducesResponseType(typeof(GetEmployeeResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status400BadRequest, Type = typeof(ValidationProblemDetails))]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult UpdateEmployee(int id, [FromBody] UpdateEmployeeRequest employeeRequest)
{
}
}
So far, so good - we've enhanced our endpoints to return more information to our Swagger UI and OpenAPI spec. But we can do better.
Introducting XML comments
XML comments are a way to add documentation to your code. They aren't required, but they're useful in the language and can be used to generate documentation for OpenAPI as well.
public class EmployeesController : BaseController
{
private readonly IRepository<Employee> _repository;
private readonly ILogger<EmployeesController> _logger;
public EmployeesController(IRepository<Employee> repository, ILogger<EmployeesController> logger)
{
_repository = repository;
_logger = logger;
}
/// <summary>
/// Get all employees.
/// </summary>
/// <returns>An array of all employees.</returns>
[HttpGet]
[ProducesResponseType(typeof(IEnumerable<GetEmployeeResponse>), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult GetAllEmployees()
{
}
/// <summary>
/// Gets an employee by ID.
/// </summary>
/// <param name="id">The ID of the employee.</param>
/// <returns>The single employee record.</returns>
[HttpGet("{id:int}")]
[ProducesResponseType(typeof(GetEmployeeResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult GetEmployeeById(int id)
{
}
/// <summary>
/// Creates a new employee.
/// </summary>
/// <param name="employeeRequest">The employee to be created.</param>
/// <returns>A link to the employee that was created.</returns>
[HttpPost]
[ProducesResponseType(typeof(GetEmployeeResponse), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> CreateEmployee([FromBody] CreateEmployeeRequest employeeRequest)
{
}
/// <summary>
/// Updates an employee.
/// </summary>
/// <param name="id">The ID of the employee to update.</param>
/// <param name="employeeRequest">The employee data to update.</param>
/// <returns></returns>
[HttpPut("{id}")]
[ProducesResponseType(typeof(GetEmployeeResponse), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult UpdateEmployee(int id, [FromBody] UpdateEmployeeRequest employeeRequest)
{
}
}
Run the project - unfortunately, we're not seeing any changes. We need to do a little more work to get our XML comments to work with OpenAPI.
Add the following to your .csproj
file:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
Unfortunately, because we turned on warnings as errors, we're seeing a lot of errors now that look like this:
warning CS1591: Missing XML comment for publicly visible type or member 'blabla'
We can fix this by adding the missing XML comments (ugh) or we can just turn off that warning for now (which is what we're going to do).
<PropertyGroup>
<NoWarn>$(NoWarn);1591</NoWarn>
</PropertyGroup>
Finally, we'll change our Program.cs
file to use the XML comments:
builder.Services.AddSwaggerGen(options =>
{
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, "TheEmployeeAPI.xml"));
});
Run the project now - you'll see your XML comments in the OpenAPI spec!
A perfect (mostly) marriage of XML comments and OpenAPI.