Building an API without documentation is like building a vehicle without a steering wheel. OpenAPI (formerly Swagger) is a specification that describes your API endpoints, HTTP verbs, and DTO schemas, allowing frontend developers and third-party integrators to consume your API instantly.
Swashbuckle is the default library integrated into ASP.NET Core. It uses Reflection to inspect your C# Controllers, parameters, and Attributes at runtime, dynamically generating a live swagger.json file and an interactive UI website.
By default, Swagger infers the data types perfectly, but it lacks human-readable descriptions. We can force Swagger to read our C# /// <summary> comments and display them in the Web UI.
In your .csproj file, add the following property to tell the C# compiler to output XML comments into a physical file:
<PropertyGroup>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<NoWarn>$(NoWarn);1591</NoWarn> <!-- Hides warnings for missing comments -->
</PropertyGroup>
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo {
Title = "Enterprise API",
Version = "v1",
Description = "Master REST API documentation"
});
// Extract the XML filepath
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
// Inject comments into Swagger
c.IncludeXmlComments(xmlPath);
});
/// <summary>
/// Retrieves a specific product by its unique ID.
/// </summary>
/// <param name="id">The physical Database ID.</param>
/// <response code="200">Returns the requested product details.</response>
/// <response code="404">If the product is not found in the database.</response>
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<ProductDto> GetProduct(int id)
{
// ...
}
A secured API will reject Swagger's requests with an HTTP 401. You must teach the Swagger UI website how to pass Bearer Tokens into the headers.
builder.Services.AddSwaggerGen(c =>
{
// 1. Define the Security Scheme (Bearer)
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme. Example: 'Bearer 12345abcdef'",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
// 2. Lock down all endpoints, requiring the token defined above
c.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
},
Scheme = "oauth2",
Name = "Bearer",
In = ParameterLocation.Header,
},
new List<string>()
}
});
});
Q: "Aside from humans reading it, what is the ultimate enterprise benefit of generating a mathematically perfect OpenAPI specification (swagger.json)?"
Architect Answer: "Client code generation. Because swagger.json represents an exact structural map of your API routes and DTOs, frontend teams can use tools like NSwag or OpenAPI Generator to instantly compile physical TypeScript or Python API client libraries in seconds. If the backend C# developer adds a new string field to a DTO, the CI/CD pipeline detects the swagger change, regenerates the TypeScript proxy clients, and immediately triggers compiler errors in the React frontend codebase alerting the UI developer of the breaking change, rather than discovering a null-reference bug in production."