dotnet add package Microsoft.AspNetCore.Mvc.Versioning
// Program.cs
builder.Services.AddApiVersioning(options =>
{
options.DefaultApiVersion = new ApiVersion(1, 0);
options.AssumeDefaultVersionWhenUnspecified = true;
options.ReportApiVersions = true;
options.ApiVersionReader = ApiVersionReader.Combine(
new UrlSegmentApiVersionReader(),
new HeaderApiVersionReader("X-Api-Version"),
new QueryStringApiVersionReader("v"));
});
[ApiVersion("1.0")]
[ApiVersion("2.0")]
[Route("api/v{v:apiVersion}/[controller]")]
public class ProductsController : ControllerBase
{
[HttpGet]
[MapToApiVersion("1.0")]
public IActionResult GetV1() => Ok(new { version = "1.0" });
[HttpGet]
[MapToApiVersion("2.0")]
public IActionResult GetV2() => Ok(new { version = "2.0", extra = "data" });
}
GET /api/products?api-version=1.0
GET /api/products?api-version=2.0
GET /api/products
X-Api-Version: 1.0
[ApiVersion("1.0")]
[ApiVersion("2.0", Deprecated = true)] // Mark as deprecated
[Route("api/v{v:apiVersion}/[controller]")]
public class ProductsController : ControllerBase { }
dotnet add package Swashbuckle.AspNetCore
// Program.cs
builder.Services.AddEndpointsApiExplorer();
builder.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"
}
});
// Add JWT Authentication to Swagger
c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
var app = builder.Build();
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "My API v1");
c.RoutePrefix = "swagger"; // Access at /swagger
});
// Program.cs
builder.Services.AddSwaggerGen(c =>
{
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
/// <summary>
/// Get all products
/// </summary>
/// <param name="category">Filter by category</param>
/// <returns>List of products</returns>
[HttpGet]
[ProducesResponseType(typeof(List<Product>), 200)]
[ProducesResponseType(400)]
public IActionResult GetProducts([FromQuery] string category) { }
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
public IActionResult Create([FromBody] Product product)
{
// ...
}
Strategy Pros Cons
URL Path Rõ ràng, dễ debug Phải update client URLs
Query String Không thay đổi URL Ít visible
Header Linh hoạt Khó debug
// ✅ Tốt - Add new fields, không break old
public class ProductResponse
{
public int Id { get; set; }
public string Name { get; set; }
// New in v2
public string Description { get; set; }
}
// ❌ Tránh - Breaking changes
// - Remove fields
// - Change field types
// - Change validation rules
# OpenAPI spec
components:
schemas:
ProductV1:
type: object
properties:
id:
type: integer
name:
type: string
ProductV2:
type: object
properties:
id:
type: integer
name:
type: string
description: # New field
type: string
Thông báo deprecation trước khi remove
Sử dụng HTTP headers để warning
Cung cấp migration guide