Swagger擴展版本控制(不符合Restful風格), 勿試…
Step1. 新增一個ApiVersions.cs檔,使用列舉(enum)定義版本的名稱
1
2
3
4
5
6
7
8
9
namespace NET6Demo.WebApi.Utility.Swagger
{
public enum ApiVersions
{
V1,
V2,
V3
}
}
Step2. 在Program.cs 中設定Swagger配置
1
2
3
4
5
6
7
8
9
10
11
builder.Services.AddSwaggerGen(option => {
#region 分版本的Swagger配置
typeof(ApiVersions).GetEnumNames().ToList().ForEach(version => {
option.SwaggerDoc(version, new OpenApiInfo() {
Title = $"RivaDemo版本{version} API文件",
Version = version,
Description = $"通用版本CoreApi版本{version}"
});
});
#endregion
});
1
2
3
4
5
6
app.UseSwaggerUI(option => {
foreach (string version in typeof(ApiVersions).GetEnumNames())
{
option.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"API版本:{version}");
}
});
Step3. 在Controller加入版次 [ApiExplorerSettings(IgnoreApi = false, GroupName = nameof(ApiVersions.V1))]
1
2
3
4
5
[Route("api/[controller]")]
[ApiController]
[ApiExplorerSettings(IgnoreApi = false, GroupName = nameof(ApiVersions.V1))]
public class UserController : ControllerBase
{ ... }
Swagger版本控制(調用第三方API版本控制)
Step1. 安裝Nuget包:
- Microsoft.AspNetCore.Mvc.Versioning
- Microsoft.AspNetCore.Mvc.Versioning.ApiExplorer
Step2. Swagger 調用第三方API版本控制
獲取APIversion中各個版本的資訊,可以利用 services.BuildServiceProvider().GetRequiredService<>獲取。
1
builder.Services.AddSwaggerGen(option => {todo...}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#region Swagger版本控制(調用第三方API版本控制)
{
//獲取API version中各個版本的資訊
var provider = builder.Services.BuildServiceProvider().GetRequiredService<IApiVersionDescriptionProvider>();
//讀取API各版本的內容生成文檔
foreach (var description in provider.ApiVersionDescriptions)
{
option.SwaggerDoc(description.GroupName, new OpenApiInfo {
Title = "RivaDemo Net6 WebAPI 文件",
Description = "RivaDemo Net6 WebAPI 文件",
Contact = new OpenApiContact { Name = "Rii", Email = "ooxx@gmail.com" },
Version = description.ApiVersion.ToString()
});
}
option.DocInclusionPredicate((version, apiDescription) => {
if (!version.Equals(apiDescription.GroupName)) return false;
IEnumerable<string>? values = apiDescription!.RelativePath
.Split('/')
.Select(e => e.Replace("v{version}", apiDescription.GroupName));
apiDescription.RelativePath = String.Join("/", values);
return true;
});
option.DescribeAllParametersInCamelCase();//參數全小寫
}
#endregion
1
app.UseSwaggerUI(option => { todo...}
1
2
3
4
5
6
7
8
9
#region Swagger版本控制(調用第三方API版本控制)
var provider = app.Services.GetRequiredService<IApiVersionDescriptionProvider>();
//默認載入最新的API版本
foreach (var description in provider.ApiVersionDescriptions.Reverse())
{
option.SwaggerEndpoint($"/swagger/{description.GroupName}/swagger.json", $"RIVA API {description.GroupName.ToUpperInvariant()}");
}
#endregion
Step3. 註冊:在programe.cs 註冊 Swagger版本控制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#region API版本控制
{
//添加支持API版本
builder.Services.AddApiVersioning(option => {
option.ReportApiVersions = true; //是否在header信息中返回API版本資訊
option.DefaultApiVersion = new ApiVersion(1,0); //默認的API版本
option.AssumeDefaultVersionWhenUnspecified = true; //未指定api版本時,設置API版本為默認的API版本
});
//配置API版本
builder.Services.AddVersionedApiExplorer(option => {
option.GroupNameFormat = "'v'VVV"; //Api版本分組名稱
option.AssumeDefaultVersionWhenUnspecified = true; //未指定api版本時,設置API版本為默認的API版本
});
}
#endregion
Step4. 套用:調用第三方API版本控制
1
2
[ApiVersion("1.0")]
[ApiVersion("2.0")]
Swagger 顯示註解
Step1. 將註解成生xml檔 路徑:專案 > 屬性 > 輸出 > 文件檔案 > 勾選「產生包含API文件檔案」 
通過這個屬性將xml檔產生出來
Step2. 在Program.cs的AddSwaggerGen讀出xml檔
1
2
3
4
5
#region Swagger顯示註解
var file = Path.Combine(AppContext.BaseDirectory, "NET6Demo.WebApi.xml"); //將xml讀取出來
option.IncludeXmlComments(file, true); //true: 顯示控制層註解
option.OrderActionsBy(o => o.RelativePath); //對action名稱進行排序
#endregion
Swagger傳入Token
1
2
3
4
5
6
7
8
9
//安全定義
option.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme {
Description = "請輸入Token,格式為: Bearer xxxxxx (注意中間必須要有空格)",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
BearerFormat = "JWT",
Scheme = "Bearer"
});
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//安全要求
option.AddSecurityRequirement(
new OpenApiSecurityRequirement {
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
new string[] {}
}
});
擴展Swagger文件上傳按鈕
- 建立一個新檔,名為: FileUploadFilter.cs
- 繼承 IOperationFilter 並實作 Apply方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
public class FileUploadFilter : IOperationFilter { /// <summary> /// 擴展文件上傳,顯示選擇文件按鈕 /// </summary> /// <param name="operation"></param> /// <param name="context"></param> /// <exception cref="NotImplementedException"></exception> void IOperationFilter.Apply(OpenApiOperation operation, OperationFilterContext context) { const string fileUploadContentType = "multipart/form-data"; //檔案為空、或非"multipart/form-data"類型的格式,不處理 if (operation.RequestBody == null || !operation.RequestBody.Content.Any(x => x.Key.Equals(fileUploadContentType, StringComparison.InvariantCultureIgnoreCase))) { return; } //限定上傳類型為IFormCollection if (context.ApiDescription.ParameterDescriptions[0].Type == typeof(IFormCollection)) { operation.RequestBody = new OpenApiRequestBody { Description = "文件上傳", Content = new Dictionary<string, OpenApiMediaType> { { fileUploadContentType, new OpenApiMediaType { Schema = new OpenApiSchema { Type ="object", Required = new HashSet<string> { "file"}, Properties = new Dictionary<string, OpenApiSchema> { { "file", new OpenApiSchema() { Type="string", Format="binary" } } } } } } } }; } } }
3.Program.cs 的 AddSwaggerGen中配置
option.OperationFilter<FileUploadFilter>();
![]()
Swagger功能封裝
將Swagger擴展方法獨立封裝成一個檔案
- 在Swagger Folder新增一個靜態類別:SwaggerExtension.cs
新增一個”Swagger完整配置”的靜態方法:AddSwaggerGenExt(this WebApplicationBuilder builder) 將Program.cs中AddSwaggerGen的Code移置該方法中
![]()
新增一個”Swagger中間件應用”的靜態方法:UseSwaggerUIExt(this WebApplication app) 將Program.cs中AddSwaggerUI的Code移置該方法中
![]()
- 在Program.cs替換成Swagger封裝的方法
![]()
全域路由前綴配置
定義一個類別實現 IApplicationModelConvention 介面,遍歷所有 Controller 來為它們加上一個前綴路由。
RouteConvention.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
/// <summary>
/// 全域路由前綴配置
/// </summary>
public class RouteConvention : IApplicationModelConvention
{
private readonly AttributeRouteModel _centralPrefix; //路由前綴變數
/// <summary>
/// 調用前傳入指定的路由前綴
/// </summary>
/// <param name="routeTemplateProvider"></param>
public RouteConvention(IRouteTemplateProvider routeTemplateProvider)
{
_centralPrefix = new AttributeRouteModel(routeTemplateProvider);
}
/// <summary>
/// 根據情況來加入api路由前綴
/// </summary>
/// <param name="application"></param>
/// <exception cref="NotImplementedException"></exception>
public void Apply(ApplicationModel application)
{
//遍歷所有的Control
foreach (var controller in application.Controllers)
{
//1.已標記 RouteAttribute的controller
var matchedSelectors = controller.Selectors.Where(e => e.AttributeRouteModel != null).ToList();
if (matchedSelectors.Any()) //判斷集合中是否有物件
{
foreach (var selectModel in matchedSelectors)
{
//在當前路由上再加上路由前綴
selectModel.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(_centralPrefix, selectModel.AttributeRouteModel);
}
}
//2.沒有標記 RouteAttribute的controller
var unmatchedSelectors = controller.Selectors.Where(e => e.AttributeRouteModel == null).ToList();
if (unmatchedSelectors.Any()) //判斷集合中是否有物件
{
foreach (var selectModel in unmatchedSelectors)
{
//加上路由前綴
selectModel.AttributeRouteModel = _centralPrefix;
}
}
}
}
}
Program.cs
1
2
3
4
builder.Services.AddControllers(option =>
{
option.Conventions.Insert(0, new RouteConvention(new RouteAttribute("RIVAapi/")));
});













