Swagger in ConnectSoft Microservice Template¶
Purpose & Overview¶
Swagger (OpenAPI) is a standard specification for describing RESTful APIs, providing machine-readable documentation that enables API discovery, testing, and client code generation. In the ConnectSoft Microservice Template, Swagger is implemented using Swashbuckle.AspNetCore, providing automatic OpenAPI specification generation, interactive API documentation via Swagger UI, and seamless integration with XML documentation comments.
Swagger provides:
- Automatic API Documentation: OpenAPI specification generated from code
- Interactive Testing: Swagger UI for testing API endpoints
- XML Comments Integration: API documentation from XML documentation comments
- Code Generation: Client SDK generation from OpenAPI specification
- API Discovery: Self-documenting APIs for developers
- gRPC Support: Swagger documentation for gRPC services via ServiceModel.Grpc
- Theming: Customizable Swagger UI themes
Swagger Philosophy
Swagger makes APIs self-documenting and discoverable. By automatically generating OpenAPI specifications from code and integrating XML documentation comments, the template ensures that API documentation is always up-to-date and comprehensive. Swagger UI provides an interactive interface for exploring and testing APIs, making it easier for developers to understand and consume the API.
Architecture Overview¶
Swagger Integration Flow¶
API Controllers (ServiceModel.RestApi)
├── XML Documentation Comments
├── Swagger Annotations
└── Data Annotations
↓
Swashbuckle.AspNetCore
├── SwaggerGen (OpenAPI Generation)
│ ├── Include XML Comments
│ ├── Enable Annotations
│ └── Configure OpenAPI Info
└── Swagger UI (Interactive Documentation)
↓
OpenAPI Specification
├── /swagger/v1/swagger.json (JSON)
└── /swagger/{documentName}/swagger.json
↓
Swagger UI
└── /swagger (Interactive UI)
Swagger Components¶
SwaggerExtensions.cs
├── AddSwagger() - Service Registration
│ ├── SwaggerGen Configuration
│ │ ├── OpenAPI Info (Title, Version)
│ │ ├── XML Comments Integration
│ │ ├── Annotations Support
│ │ └── Inheritance Configuration
│ └── ServiceModel.Grpc Integration (if UseGrpc)
├── UseMicroserviceSwagger() - Middleware
│ ├── UseSwagger() - JSON Endpoint
│ └── UseSwaggerUI() - Interactive UI
│ ├── Theme Configuration
│ ├── Endpoint Configuration
│ └── UI Options
└── MapMicroserviceSwagger() - Endpoint Mapping
└── Rate Limiting Exemption
SwaggerOptions.cs
├── EnableSwagger (bool)
├── EnableSwaggerUI (bool)
└── SwaggerUITheme (string?)
Service Registration¶
AddSwagger Extension¶
Swagger is registered via AddSwagger() extension method:
// MicroserviceRegistrationExtensions.cs
#if Swagger && (UseGrpc || UseRestApi || UseAzureFunction)
services.AddSwagger();
#endif
Implementation:
// SwaggerExtensions.cs
internal static IServiceCollection AddSwagger(this IServiceCollection services)
{
ArgumentNullException.ThrowIfNull(services);
if (OptionsExtensions.SwaggerOptions.EnableSwagger)
{
// Find XML documentation files
string serviceModelXmlCommentsFilePath = null;
string apiModelXmlCommentsFilePath = null;
// ServiceModel XML comments
string[] files = Directory.GetFiles(
AppDomain.CurrentDomain.BaseDirectory,
searchPattern: "ConnectSoft.MicroserviceTemplate.ServiceModel.xml",
searchOption: SearchOption.AllDirectories);
if (files != null && files.Length == 1)
{
serviceModelXmlCommentsFilePath = files[0];
}
// RestApi XML comments
files = Directory.GetFiles(
AppDomain.CurrentDomain.BaseDirectory,
searchPattern: "ConnectSoft.MicroserviceTemplate.ServiceModel.RestApi.xml",
searchOption: SearchOption.AllDirectories);
if (files != null && files.Length == 1)
{
apiModelXmlCommentsFilePath = files[0];
}
#if UseGrpc
// ServiceModel.Grpc Swagger integration
if (OptionsExtensions.SwaggerOptions.EnableSwagger)
{
services.AddServiceModelGrpcSwagger();
}
#endif
services.AddSwaggerGen(c =>
{
// OpenAPI document information
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "ConnectSoft.MicroserviceTemplate's microservice back-end API",
Version = "v1"
});
// Include XML comments from ServiceModel
if (!string.IsNullOrEmpty(serviceModelXmlCommentsFilePath))
{
c.IncludeXmlComments(serviceModelXmlCommentsFilePath);
}
// Include XML comments from RestApi
if (!string.IsNullOrEmpty(apiModelXmlCommentsFilePath))
{
c.IncludeXmlComments(apiModelXmlCommentsFilePath);
}
// Enable Swagger annotations
c.EnableAnnotations(enableAnnotationsForInheritance: true, enableAnnotationsForPolymorphism: true);
// Use allOf for inheritance (OpenAPI 3.0)
c.UseAllOfForInheritance();
#if UseGrpc
// Force wrapped JSON for gRPC operations
c.OperationFilter<ForceWrappedJsonForGrpcFilter>();
#endif
});
}
return services;
}
Key Features:
- Conditional Registration: Only registers if EnableSwagger is true
- XML Comments: Automatically finds and includes XML documentation files
- gRPC Support: Integrates ServiceModel.Grpc Swagger support
- Annotations: Enables Swagger annotations for enhanced documentation
Middleware Configuration¶
UseMicroserviceSwagger Middleware¶
Swagger middleware is registered in the application pipeline:
// MicroserviceRegistrationExtensions.cs
#if Swagger && (UseGrpc || UseRestApi || UseAzureFunction)
application.UseMicroserviceSwagger();
#endif
Implementation:
// SwaggerExtensions.cs
internal static IApplicationBuilder UseMicroserviceSwagger(this IApplicationBuilder application)
{
ArgumentNullException.ThrowIfNull(application);
SwaggerOptions swaggerOptions = application.ApplicationServices
.GetRequiredService<IOptions<SwaggerOptions>>().Value;
if (swaggerOptions.EnableSwagger)
{
// Enable middleware to serve generated Swagger as a JSON endpoint
application.UseSwagger();
if (swaggerOptions.EnableSwaggerUI)
{
#if UseGrpc
// Enable ServiceModel.Grpc HTTP/1.1 JSON gateway for Swagger UI
// This allows "Try it out" functionality for gRPC services
application.UseServiceModelGrpcSwaggerGateway();
#endif
// Enable middleware to serve swagger-ui (HTML, JS, CSS, etc.)
application.UseSwaggerUI(options =>
{
// Set the Swagger UI browser document title
options.DocumentTitle = "ConnectSoft.MicroserviceTemplate's microservice back-end API";
// Inject custom theme (optional)
if (!string.IsNullOrEmpty(swaggerOptions.SwaggerUITheme))
{
// Example: /swagger-ui/themes/3.x/theme-monokai.css
options.InjectStylesheet(swaggerOptions.SwaggerUITheme);
}
// Configure Swagger JSON endpoint
options.SwaggerEndpoint(
"/swagger/v1/swagger.json",
"ConnectSoft.MicroserviceTemplate's microservice back-end API.");
// Display operation IDs
options.DisplayOperationId();
// Display request duration
options.DisplayRequestDuration();
});
}
}
return application;
}
Middleware Order: - Swagger middleware should be placed before routing - Typically placed after exception handling and logging - Before authentication/authorization middleware
Endpoint Mapping¶
MapMicroserviceSwagger Extension¶
Swagger endpoints are mapped in the endpoint configuration:
// MicroserviceRegistrationExtensions.cs
#if Swagger && (UseGrpc || UseRestApi || UseAzureFunction)
endpoints.MapMicroserviceSwagger();
#endif
Implementation:
// SwaggerExtensions.cs
internal static IEndpointRouteBuilder MapMicroserviceSwagger(this IEndpointRouteBuilder endpoints)
{
ArgumentNullException.ThrowIfNull(endpoints);
SwaggerOptions swaggerOptions = endpoints.ServiceProvider
.GetRequiredService<IOptions<SwaggerOptions>>().Value;
if (swaggerOptions.EnableSwagger)
{
var endpointsBuilder = endpoints.MapSwagger();
#if RateLimiting
// Disable rate limiting for Swagger endpoints
endpointsBuilder.DisableRateLimiting();
#endif
}
return endpoints;
}
Available Endpoints:
- /swagger/v1/swagger.json - OpenAPI JSON specification
- /swagger/{documentName}/swagger.json - Versioned OpenAPI specification
- /swagger - Swagger UI (if EnableSwaggerUI is true)
Configuration¶
SwaggerOptions¶
Swagger configuration is managed through SwaggerOptions:
// SwaggerOptions.cs
public sealed class SwaggerOptions
{
public const string SwaggerOptionsSectionName = "Swagger";
[Required]
required public bool EnableSwagger { get; set; }
[Required]
required public bool EnableSwaggerUI { get; set; }
public string? SwaggerUITheme { get; set; }
}
Configuration File¶
appsettings.json:
{
"Swagger": {
"EnableSwagger": true,
"EnableSwaggerUI": true,
"SwaggerUITheme": "/swagger-ui/themes/3.x/theme-monokai.css"
}
}
Environment-Specific Configuration:
// appsettings.Development.json
{
"Swagger": {
"EnableSwagger": true,
"EnableSwaggerUI": true
}
}
// appsettings.Production.json
{
"Swagger": {
"EnableSwagger": true,
"EnableSwaggerUI": false // Disable UI in production
}
}
Configuration Properties:
| Property | Type | Required | Description |
|---|---|---|---|
EnableSwagger |
bool |
Yes | Enables Swagger JSON endpoint generation |
EnableSwaggerUI |
bool |
Yes | Enables Swagger UI interactive documentation |
SwaggerUITheme |
string? |
No | Path to custom CSS theme for Swagger UI |
XML Documentation Integration¶
Enabling XML Documentation¶
Project Configuration:
Directory.Build.props (applied to all projects):
XML Documentation Files¶
Swagger automatically locates and includes XML documentation files:
- ServiceModel.xml: DTOs and service model documentation
- Pattern:
ConnectSoft.MicroserviceTemplate.ServiceModel.xml -
Location:
AppDomain.CurrentDomain.BaseDirectory -
RestApi.xml: REST API controllers and endpoints documentation
- Pattern:
ConnectSoft.MicroserviceTemplate.ServiceModel.RestApi.xml - Location:
AppDomain.CurrentDomain.BaseDirectory
Writing XML Comments¶
Controller Documentation:
/// <summary>
/// Processes microservice aggregate root operations.
/// </summary>
/// <remarks>
/// This controller handles creation, update, and deletion of aggregate roots
/// following Domain-Driven Design principles.
/// </remarks>
[ApiController]
[Route("api/[controller]")]
public class MicroserviceAggregateRootsServiceController : ControllerBase
{
/// <summary>
/// Creates a new microservice aggregate root.
/// </summary>
/// <param name="request">The creation request containing aggregate root details.</param>
/// <param name="token">Cancellation token to cancel the operation.</param>
/// <returns>The created aggregate root.</returns>
/// <exception cref="ValidationException">Thrown when input validation fails.</exception>
/// <exception cref="DomainModelException">Thrown when business rules are violated.</exception>
[HttpPost("MicroserviceAggregateRoots/", Name = nameof(CreateMicroserviceAggregateRoot))]
[SwaggerOperation(
Summary = "Process, create and store a MicroserviceAggregateRoots.",
Description = "Process, create and store a MicroserviceAggregateRoots.",
OperationId = nameof(CreateMicroserviceAggregateRoot),
Tags = ["MicroserviceAggregateRoots"])]
[SwaggerResponse(StatusCodes.Status200OK, "Created MicroserviceAggregateRoot.", typeof(CreateMicroserviceAggregateRootResponse))]
[SwaggerResponse(StatusCodes.Status400BadRequest, "The request is invalid.", typeof(ValidationProblemDetails))]
[SwaggerResponse(StatusCodes.Status500InternalServerError, "An un-handled exception occurred.", typeof(ProblemDetails))]
public async Task<CreateMicroserviceAggregateRootResponse> CreateMicroserviceAggregateRoot(
[FromBody, SwaggerRequestBody("Create MicroserviceAggregateRoot request payload", Required = true)]
CreateMicroserviceAggregateRootRequest request,
CancellationToken token = default)
{
// Implementation
}
}
DTO Documentation:
/// <summary>
/// Request DTO for creating a microservice aggregate root.
/// </summary>
public class CreateMicroserviceAggregateRootRequest
{
/// <summary>
/// Gets or sets the unique identifier for the aggregate root.
/// </summary>
/// <value>
/// A <see cref="Guid"/> that uniquely identifies the aggregate root.
/// </value>
[Required]
public Guid ObjectId { get; set; }
/// <summary>
/// Gets or sets the name of the aggregate root.
/// </summary>
/// <value>
/// A string representing the name of the aggregate root.
/// </value>
[Required]
[StringLength(100)]
public string Name { get; set; }
}
Swagger Annotations¶
SwaggerOperation Attribute¶
Usage:
[SwaggerOperation(
Summary = "Short summary of the operation",
Description = "Detailed description of the operation",
OperationId = "UniqueOperationId",
Tags = ["Tag1", "Tag2"])]
[HttpPost]
public async Task<ActionResult> Create(CreateRequest request)
{
// Implementation
}
SwaggerResponse Attribute¶
Usage:
[SwaggerResponse(StatusCodes.Status200OK, "Success description", typeof(ResponseDto))]
[SwaggerResponse(StatusCodes.Status400BadRequest, "Bad request description", typeof(ValidationProblemDetails))]
[SwaggerResponse(StatusCodes.Status404NotFound, "Not found description")]
[SwaggerResponse(StatusCodes.Status500InternalServerError, "Error description", typeof(ProblemDetails))]
[HttpGet("{id}")]
public async Task<ActionResult<ResponseDto>> Get(Guid id)
{
// Implementation
}
SwaggerRequestBody Attribute¶
Usage:
[HttpPost]
public async Task<ActionResult> Create(
[FromBody, SwaggerRequestBody("Description of request body", Required = true)]
CreateRequest request)
{
// Implementation
}
SwaggerSchemaAttribute¶
Usage:
[SwaggerSchema(Description = "Description of the schema", Title = "Schema Title")]
public class MyDto
{
// Properties
}
Swagger UI Features¶
Interactive Testing¶
Swagger UI provides "Try it out" functionality: - Test API endpoints directly from the browser - View request/response examples - See response status codes and headers - Test authentication (if configured)
Operation Details¶
Each operation displays: - Summary: Brief description - Description: Detailed description - Parameters: Request parameters with types and descriptions - Request Body: Request body schema and examples - Responses: Response schemas and status codes - Operation ID: Unique identifier for the operation
Schema Viewer¶
Swagger UI displays: - Data Models: All DTOs and models - Schema Details: Properties, types, and descriptions - Example Values: Sample values for each property - Inheritance: Parent-child relationships
gRPC Integration¶
ServiceModel.Grpc Swagger Support¶
When UseGrpc is enabled, Swagger integrates with ServiceModel.Grpc:
#if UseGrpc
// Add ServiceModel.Grpc Swagger integration
services.AddServiceModelGrpcSwagger();
// Enable HTTP/1.1 JSON gateway for Swagger UI
application.UseServiceModelGrpcSwaggerGateway();
// Force wrapped JSON for gRPC operations
c.OperationFilter<ForceWrappedJsonForGrpcFilter>();
#endif
Features: - gRPC services documented in Swagger - "Try it out" functionality via HTTP/1.1 JSON gateway - Automatic conversion between gRPC and REST representations
Customization¶
Custom Themes¶
Swagger UI supports custom themes:
Configuration:
Custom Theme Files:
- Place theme CSS files in wwwroot/swagger-ui/themes/
- Reference in SwaggerUITheme configuration
- Themes available: monokai, material, dark, etc.
Additional SwaggerGen Options¶
Advanced Configuration:
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "My API",
Version = "v1",
Description = "API Description",
Contact = new OpenApiContact
{
Name = "Support",
Email = "support@example.com"
},
License = new OpenApiLicense
{
Name = "MIT",
Url = new Uri("https://opensource.org/licenses/MIT")
}
});
// Security definitions
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
// Security requirements
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
Security Considerations¶
Production Deployment¶
Best Practices:
-
Disable Swagger UI in Production:
-
Use Authentication Middleware:
-
Restrict Access by IP:
-
Use Authorization Policies:
Rate Limiting¶
Swagger endpoints are exempt from rate limiting:
var endpointsBuilder = endpoints.MapSwagger();
#if RateLimiting
endpointsBuilder.DisableRateLimiting();
#endif
This ensures that API documentation is always accessible, even under load.
Best Practices¶
Do's¶
-
Enable XML Documentation
-
Write Comprehensive XML Comments
-
Use Swagger Annotations
-
Document All Response Codes
-
Use Descriptive Operation IDs
-
Group Operations with Tags
Don'ts¶
-
Don't Expose Swagger UI in Production Without Authentication
-
Don't Skip XML Documentation
-
Don't Use Generic Operation IDs
-
Don't Document Secrets in Examples
Integration with Scalar¶
Coexistence¶
Swagger UI and Scalar can coexist:
#if Swagger && (UseGrpc || UseRestApi || UseAzureFunction)
endpoints.MapMicroserviceSwagger();
#endif
#if Scalar && (UseGrpc || UseRestApi || UseAzureFunction)
endpoints.MapScalar();
#endif
Benefits: - Provide choice to API consumers - Different teams can prefer different tools - Gradual migration from Swagger UI to Scalar
Shared OpenAPI Specification¶
Both Swagger UI and Scalar use the same OpenAPI specification:
For detailed information on Scalar, see Scalar.
Troubleshooting¶
Issue: Swagger Not Loading¶
Symptoms: Swagger endpoints return 404 or not accessible.
Solutions:
1. Verify EnableSwagger is true in configuration
2. Check feature flags: #if Swagger && (UseGrpc || UseRestApi || UseAzureFunction)
3. Verify middleware is registered: application.UseMicroserviceSwagger()
4. Check endpoint mapping: endpoints.MapMicroserviceSwagger()
Issue: XML Comments Not Appearing¶
Symptoms: XML documentation comments not showing in Swagger UI.
Solutions:
1. Verify GenerateDocumentationFile is true in project file
2. Check XML files are generated in output directory
3. Verify XML files match expected patterns:
- ConnectSoft.MicroserviceTemplate.ServiceModel.xml
- ConnectSoft.MicroserviceTemplate.ServiceModel.RestApi.xml
4. Check XML files are included in deployment package
Issue: Swagger UI Not Displaying¶
Symptoms: Swagger JSON endpoint works, but UI doesn't load.
Solutions:
1. Verify EnableSwaggerUI is true in configuration
2. Check UseSwaggerUI() is called in middleware pipeline
3. Verify Swagger UI assets are accessible
4. Check browser console for JavaScript errors
Issue: gRPC Operations Not Documented¶
Symptoms: gRPC services not appearing in Swagger.
Solutions:
1. Verify UseGrpc feature flag is enabled
2. Check AddServiceModelGrpcSwagger() is called
3. Verify UseServiceModelGrpcSwaggerGateway() is in middleware pipeline
4. Check ForceWrappedJsonForGrpcFilter is added
Issue: Custom Theme Not Applied¶
Symptoms: Custom theme CSS not loading.
Solutions:
1. Verify theme file path is correct
2. Check theme file is in wwwroot/swagger-ui/themes/
3. Verify SwaggerUITheme configuration matches file path
4. Check file permissions and static file serving
Related Documentation¶
- REST API: REST API implementation and controllers
- Scalar: Modern alternative to Swagger UI
- Documentation as Code: XML documentation and API documentation
- API Versioning: API versioning strategies
- Security: Security best practices for API documentation
Summary¶
Swagger in the ConnectSoft Microservice Template provides:
- ✅ Automatic Documentation: OpenAPI specification generated from code
- ✅ XML Comments Integration: API documentation from XML comments
- ✅ Interactive Testing: Swagger UI for testing endpoints
- ✅ gRPC Support: Documentation for gRPC services
- ✅ Customization: Themes and advanced configuration options
- ✅ Security: Configurable authentication and authorization
- ✅ Best Practices: Comprehensive documentation patterns
By following these patterns, teams can:
- Document APIs Automatically: Keep documentation in sync with code
- Enable API Discovery: Self-documenting APIs for developers
- Facilitate Testing: Interactive testing via Swagger UI
- Generate Client Code: SDK generation from OpenAPI specification
- Maintain Consistency: Standardized API documentation approach
Swagger is an essential tool for API development, providing comprehensive documentation, interactive testing, and client code generation capabilities that make APIs more accessible and easier to consume.