.Net Core3.0 WebApi 二:API 文檔神器 Swagger


.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.csConfigure里的路由模版注釋掉,改成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>();

運行一下,看看效果:

 

 這樣,枚舉的的描述也就展示出來了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM