.Net Core3.0 WebApi 目錄
之前一篇介紹過了Swagger,也可以參照這篇。
ASP.NET Core 3.0 WebApi中使用Swagger生成API文檔簡介
為什么使用Swagger
隨着互聯網技術的發展,現在的網站架構基本都由原來的后端渲染,變成了:前端渲染、后端分離的形態,而且前端技術和后端技術在各自的道路上越走越遠。
前端和后端的唯一聯系,變成了API接口;API文檔變成了前后端開發人員聯系的紐帶,變得越來越重要,swagger就是一款讓你更好的書寫API文檔的框架。
沒有API文檔工具之前,大家都是手寫API文檔的,在什么地方書寫的都有,有在confluence上寫的,有在對應的項目目錄下readme.md上寫的,每個公司都有每個公司的玩法,無所謂好壞。
書寫API文檔的工具有很多,但是能稱之為“框架”的,估計也只有swagger了。
添加Swagger包
Nuget搜索Swashbuckle.AspNetCore
注冊Swagger服務
打開Startup.cs類,編輯 ConfigureServices 類
public void ConfigureServices(IServiceCollection services) { var ApiName = "Webapi.Core"; services.AddSwaggerGen(c => { c.SwaggerDoc("V1", new OpenApiInfo { // {ApiName} 定義成全局變量,方便修改 Version = "V1", Title = $"{ApiName} 接口文檔——Netcore 3.0", Description = $"{ApiName} HTTP API V1", }); c.OrderActionsBy(o => o.RelativePath); }); services.AddControllers(); }
光一個注冊Swagger就這么多代碼?如果我再注冊其他的服務,豈不是讓ConfigureServices有N行代碼。。。。。。,雖然我們可以使用把代碼放在#region和#endregion里,但是如果可以將每個服務的配置,都封裝到一個方法里面,豈不美哉。如果我需要修改服務的配置代碼,只需要到對應的方法里面去修改,而不是去ConfigureServices這個方法里面找到需要修改的服務再去修改,而ConfigureServices里只需要services.addxxx()就行了,代碼簡約美觀直視。
原來還真有辦法,新建SetUp文件夾,存放要注冊的服務,我們新建一個靜態類SwaggerSetUp.cs,新建靜態方法AddSwaggerSetup,用來注冊Swagger。
namespace WebApi.Core.Api.SetUp { public static class SwaggerSetUp { public static void AddSwaggerSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); var ApiName = "Webapi.Core"; services.AddSwaggerGen(c => { c.SwaggerDoc("V1", new OpenApiInfo { // {ApiName} 定義成全局變量,方便修改 Version = "V1", Title = $"{ApiName} 接口文檔——Netcore 3.0", Description = $"{ApiName} HTTP API V1", }); c.OrderActionsBy(o => o.RelativePath); }); } } }
其實就是給 IServiceCollection 增加了一個擴展方法,我們在使用的時候,就很簡單了,擴展方法,相信大家都會使用的。
啟動Swagger
編輯Configure方法
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseSwagger(); app.UseSwaggerUI(c => { c.SwaggerEndpoint($"/swagger/V1/swagger.json", "WebApi.Core V1"); //路徑配置,設置為空,表示直接在根域名(localhost:8001)訪問該文件,注意localhost:8001/swagger是訪問不到的,去launchSettings.json把launchUrl去掉,如果你想換一個路徑,直接寫名字即可,比如直接寫c.RoutePrefix = "doc"; c.RoutePrefix = "";
//c.DocExpansion(Swashbuckle.AspNetCore.SwaggerUI.DocExpansion.None);
c.DefaultModelsExpandDepth(0);
//說明:
//1、DocExpansion設置為none可折疊所有方法
//2、DefaultModelsExpandDepth設置為-1 可不顯示models
}); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { //endpoints.MapControllers(); endpoints.MapControllerRoute( name: "default", pattern: "{controller=Home}/{action=Index}/{id?}" ); }); }
運行項目,就出來了,http://localhost:xxxx/index.html
為什么只有天氣的?我新建的User控制器呢?還有我辛苦編寫的hello方法,都沒有了。。
解決方法:將Startup.cs
中Configure
里的路由模版注釋掉,改成endpoints.MapControllers();
,增加BaseController.cs
並繼承ControllerBase
,然后在BaseController
設置路由模版,讓Controller
繼承BaseController
/// <summary> /// 自定義路由模版 /// </summary> [Route("api/[controller]/[action]")] [ApiController] public class BaseController : ControllerBase { }
/// <summary> /// 用戶 /// </summary> //[Route("api/[controller]")] //[ApiController] public class UserController : BaseController
這樣就出現了。可以嘗試調試一下,親測可行。
在上邊的截圖中,我們可以看到,已經生成了一個 api 列表,我們不僅可以清晰的看到項目中含有那些接口,還可以直接點擊發送請求,類似 postman 那樣,做接口調試,
但是現在有兩個問題:
1、這個接口文檔現在還不多,如果多了的話,每個接口對應的意義可能會混淆,
2、另外,這個接口文檔,也是方便前端工程師查看的,目前這個這個樣式,看起來是挺費解的。
添加接口注釋
右鍵項目名稱=>屬性=>生成,勾選“輸出”下面的“xml文檔文件”,這里我用的是相對路徑。
現在呢,配置好了xml文件,接下來需要讓系統啟動的時候,去讀取這個文件了,重新編輯SwaggerSetUp.cs,修改AddSwaggerSetup函數,注意配置的參數 true:
public static class SwaggerSetUp { public static void AddSwaggerSetup(this IServiceCollection services) { if (services == null) throw new ArgumentNullException(nameof(services)); var ApiName = "Webapi.Core"; services.AddSwaggerGen(c => { c.SwaggerDoc("V1", new OpenApiInfo { // {ApiName} 定義成全局變量,方便修改 Version = "V1", Title = $"{ApiName} 接口文檔——Netcore 3.0", Description = $"{ApiName} HTTP API V1", }); c.OrderActionsBy(o => o.RelativePath); // 獲取xml注釋文件的目錄 var xmlPath = Path.Combine(AppContext.BaseDirectory, "WebApi.Core.Api.xml"); c.IncludeXmlComments(xmlPath, true);//默認的第二個參數是false,這個是controller的注釋,記得修改 }); } }
給我們的控制器都加上注釋,運行項目,發現頁面注釋都加上了
我們新建一個實體,看看,這些注釋有沒有加上去。
首先,在Entity新建一個類庫,注意,是Core版本的類庫。WebApi.Core.Models
新建實體類User
namespace WebApi.Core.Models { /// <summary> /// 用戶表 /// </summary> public class User { /// <summary> /// id /// </summary> public int Id { get; set; } /// <summary> /// 姓名 /// </summary> public string Name { get; set; } /// <summary> /// 年齡 /// </summary> public int Age { get; set; } } }
右鍵項目屬性,生成 XML
編輯SwaggerSetUp.cs,修改AddSwaggerSetup函數,添加以下代碼
var xmlModelPath = Path.Combine(AppContext.BaseDirectory, "WebApi.Core.Models.xml");//這個就是Models層的xml文件名 c.IncludeXmlComments(xmlModelPath);
UserController新建一個Action
運行項目,發現實體類也有了注釋
去掉警告
我們發現,代碼中,有好多地方,會下滑的波浪線,而且生成項目會有一些警告,提示我們加注釋。這個對於有點強迫症的人,是很難接受的。
下面我們就去掉警告。
右鍵項目 屬性 -》 Errors and warnings 配置 1591:、
這樣就OK了。
隱藏接口
如果不想顯示某些接口,直接在controller 上,或者action 上,增加特性
[ApiExplorerSettings(IgnoreApi = true)]
【補充】
現在這些實體類的注釋是有了,但是,項目中難免會用到枚舉,這個時候,也需要將枚舉的注釋顯示出來。
這里通過了反射程序集,獲取所有枚舉定義放入字典,然后通過OpenApiSchema.Key來查表找到對應的枚舉類型,通過Microsoft.OpenApi.Any.OpenApiInteger.Value強轉會枚舉,再補寫枚舉對應的描述。
我們簡單定義一個枚舉:
public enum ProjectTypeEnum { /// <summary> /// 枚舉一 /// </summary> [Description("枚舉一")] Enum1=1, /// <summary> /// 枚舉二 /// </summary> [Description("枚舉二")] Enum2 = 2, /// <summary> /// 枚舉三 /// </summary> [Description("枚舉三")] Enum3 = 3 }
我們需要一個filter,如下:
/// <summary> /// Add enum value descriptions to Swagger /// </summary> public class EnumDocumentFilter : IDocumentFilter { public void Apply(Microsoft.OpenApi.Models.OpenApiDocument swaggerDoc, DocumentFilterContext context) { Dictionary<string, Type> dict = GetAllEnum(); foreach (var item in swaggerDoc.Components.Schemas) { var property = item.Value; var typeName = item.Key; Type itemType = null; if (property.Enum != null && property.Enum.Count > 0) { if (dict.ContainsKey(typeName)) { itemType = dict[typeName]; } else { itemType = null; } List<OpenApiInteger> list = new List<OpenApiInteger>(); foreach (var val in property.Enum) { list.Add((OpenApiInteger)val); } property.Description += DescribeEnum(itemType, list); } } } private static Dictionary<string, Type> GetAllEnum() { Assembly ass = Assembly.Load("WebApi.Core.Models"); Type[] types = ass.GetTypes(); Dictionary<string, Type> dict = new Dictionary<string, Type>(); foreach (Type item in types) { if (item.IsEnum) { dict.Add(item.Name, item); } } return dict; } private static string DescribeEnum(Type type, List<OpenApiInteger> enums) { var enumDescriptions = new List<string>(); foreach (var item in enums) { if (type == null) continue; var value = Enum.Parse(type, item.Value.ToString()); var desc = GetDescription(type, value); if (string.IsNullOrEmpty(desc)) enumDescriptions.Add($"{item.Value.ToString()}:{Enum.GetName(type, value)}; "); else enumDescriptions.Add($"{item.Value.ToString()}:{Enum.GetName(type, value)},{desc}; "); } return $"<br/>{Environment.NewLine}{string.Join("<br/>" + Environment.NewLine, enumDescriptions)}"; } private static string GetDescription(Type t, object value) { foreach (MemberInfo mInfo in t.GetMembers()) { if (mInfo.Name == t.GetEnumName(value)) { foreach (Attribute attr in Attribute.GetCustomAttributes(mInfo)) { if (attr.GetType() == typeof(DescriptionAttribute)) { return ((DescriptionAttribute)attr).Description; } } } } return string.Empty; } }
然后,在swagger注冊的里面,使用這個filter:
var xmlModelPath = Path.Combine(AppContext.BaseDirectory, "YX.APPApi.Models.xml");//這個就是Models層的xml文件名 c.IncludeXmlComments(xmlModelPath); c.DocumentFilter<EnumDocumentFilter>();
運行一下,看看效果:
這樣,枚舉的的描述也就展示出來了。