ASP.NET Core 奇淫技巧之動態WebApi

曉晨Master發表於2019-06-12

一.前言

接觸到動態WebApi(Dynamic Web API)這個詞的已有幾年,是從ABP框架裡面接觸到的,當時便對ABP的這個技術很好奇,後面分析了一波,也嘗試過從ABP剝離一個出來作為獨立元件來使用,可是後來因與ABP依賴太多而放棄。十幾天前朋友 熊貓 將這部分程式碼(我和他在搞事情)成功的從 ABP 中剝離出來並做了一個簡單Demo扔給我,經過這麼久(實在是太懶^_^)終於經過一些修改、新增功能、封裝,現在已經能作為一個獨立元件使用,專案開源在Github(https://github.com/dotnetauth/Panda.DynamicWebApi),希望覺得有用的朋友能給一個 Star 支援一下。

本文只講使用,不講原理,原理放到後面的文章中詳細介紹。

二.介紹

不管是傳統的三層架構、 DDD 經典四層架構(DDD Lite),亦或是其他具有應用邏輯層(業務邏輯層)的架構,在Web應用程式開發當中 ,我們的業務邏輯最終都需要經過 Web Api 來進行呼叫,這裡我們可能會有一個重複的操作:編寫業務邏輯->編寫API呼叫業務邏輯,這種重複性的操作有沒有解決辦法呢,我們編寫完業務邏輯以後便給我們自動生成WebApi,答案當然是有的。

這裡介紹一下本文的主角:Panda.DynamicWebApihttps://github.com/dotnetauth/Panda.DynamicWebApi)。源自於ABP的一個可獨立使用的,可自動為你的業務邏輯層生成 ASP.NET Core WebApi 層的開源元件。它生成的API符合Restful風格,可以根據符合條件的類來生成WebApi,由MVC框架直接呼叫邏輯,無效能問題,完美相容Swagger來構建API說明文件。

三.使用

這裡以 DDD 經典四層架構中的應用邏輯層來講解。

1.準備

(1)建立兩個專案一個是應用邏輯層類庫專案;一個是作為生成WebApi Host,ASP.NET Core WebApi專案

1560273021487

(2)編寫應用邏輯

定義一個應用邏輯介面,所有應用邏輯都應實現它:

public interface IApplicationService
{
}

定義一個學生管理邏輯介面,繼承應用邏輯介面

public interface IStudentAppService : IApplicationService
{
    /// <summary>
    /// 根據ID獲取學生
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    StudentOutput Get(int id);

    /// <summary>
    /// 獲取所有學生
    /// </summary>
    /// <returns></returns>
    List<StudentOutput> Get();

    /// <summary>
    /// 更新學生資訊
    /// </summary>
    /// <param name="input"></param>
    void Update(UpdateStudentInput input);

    /// <summary>
    /// 更新學生年齡
    /// </summary>
    /// <param name="age"></param>
    void UpdateAge(int age);

    /// <summary>
    /// 根據ID刪除學生
    /// </summary>
    /// <param name="id"></param>
    void Delete(int id);

    /// <summary>
    /// 新增學生
    /// </summary>
    /// <param name="input"></param>
    void Create(CreateStudentInput input);
}

實現學生邏輯管理介面:

public class StudentAppService: IStudentAppService
{
    /// <summary>
    /// 根據ID獲取學生
    /// </summary>
    /// <param name="id"></param>
    /// <returns></returns>
    [HttpGet("{id:int}")]
    public StudentOutput Get(int id)
    {
        return new StudentOutput() {Id = 1, Age = 18, Name = "張三"};
    }

    /// <summary>
    /// 獲取所有學生
    /// </summary>
    /// <returns></returns>
    public List<StudentOutput> Get()
    {
        return new List<StudentOutput>()
        {
            new StudentOutput(){Id = 1,Age = 18,Name = "張三"},
            new StudentOutput(){Id = 2,Age = 19,Name = "李四"}
        };
    }

    /// <summary>
    /// 更新學生資訊
    /// </summary>
    /// <param name="input"></param>
    public void Update(UpdateStudentInput input)
    {
        throw new System.NotImplementedException();
    }

    /// <summary>
    /// 更新學生年齡
    /// </summary>
    /// <param name="age"></param>
    [HttpPatch("{id:int}/age")]
    public void UpdateAge(int age)
    {
        throw new System.NotImplementedException();
    }

    /// <summary>
    /// 根據ID刪除學生
    /// </summary>
    /// <param name="id"></param>
    [HttpDelete("{id:int}")]
    public void Delete(int id)
    {
        throw new System.NotImplementedException();
    }

    /// <summary>
    /// 新增學生
    /// </summary>
    /// <param name="input"></param>
    public void Create(CreateStudentInput input)
    {
        throw new System.NotImplementedException();
    }
}

(3)給 WebApi Host 專案配置 Swagger。

Install-Package Swashbuckle.AspNetCore -Version 4.0.1

Startup 中配置

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

    services.AddSwaggerGen(options =>
    {
        options.SwaggerDoc("v1", new Info { Title = "曉晨學生管理系統 WebApi", Version = "v1" });

        options.DocInclusionPredicate((docName, description) => true);

        options.IncludeXmlComments(@"bin\Debug\netcoreapp2.2\Xc.StuMgr.WebApiHost.xml");
        options.IncludeXmlComments(@"bin\Debug\netcoreapp2.2\Xc.StuMgr.Application.xml");
    });
}

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseSwagger();
    app.UseSwaggerUI(c =>
    {
        c.SwaggerEndpoint("/swagger/v1/swagger.json", "曉晨學生管理系統 WebApi");
    });

    app.UseMvc();
}

執行則會直接看到預設的 ValuesController 的5個API。

2.動態WebApi

通過Nuget 為 Application 專案安裝元件:

Install-Package Panda.DynamicWebApi

為介面 IApplicationService繼承 IDynamicWebApi同時新增特性DynamicWebApi

[DynamicWebApi]
public interface IApplicationService:IDynamicWebApi
{
}

在 WebApi Host 專案中,Startup裡配置動態WebApi:

Startup.cs:

// 新增動態WebApi 需放在 AddMvc 之後
services.AddDynamicWebApi();

然後開啟瀏覽器訪問將會看到:

1560277564506

可以看到成功為我們的 StudentAppService 生成了WebApi,並且和Swagger完美相容。

四.詳細介紹

經過上面的介紹,大家應該可以看出使用是非常簡單的,只需兩步:

第一步:為你的類(或者該類的介面、該類繼承的抽象類,不得放在該類前面兩種情況的父類上)繼承 IDynamicWebApi介面並加入特性[DynamicWebApi]

第二步:Startup中註冊

// 新增動態WebApi 需放在 AddMvc 之後
services.AddDynamicWebApi();

因為需要MVC的一些類來進行處理,所以必須放在AddMvc之後,本元件有檢查。

1.規則

本元件採用約定大於配置,所以在實際使用中有幾個規則:

(1)要讓類生成動態API需要滿足兩個條件,一個是該類直接間接實現 IDynamicWebApi,同時該類本身或者父抽象類或者實現的介面具有特性 DynamicWebApi

(2)新增特性 [NonDynamicWebApi] 可使一個類或者一個方法不生成API,[NonDynamicWebApi] 具有最高的優先順序。

(3)會對符合規則的動態API類名進行字尾的刪除,如:我們前面的 StudentAppService,會被刪除 AppService 字尾,這個規則是可以動態配置的。

(4)會自動新增API路由字首,預設會為所有API新增 api字首

(5)預設的HTTP動詞為POST,可以通過 HttpGet/HttpPost/HttpDelete等等ASP.NET Core 內建特性來覆蓋

(6)可以通過HttpGet/HttpPost/HttpDelete等內建特性來覆蓋預設路由

(7)預設會根據你的方法名字來設定HTTP動詞,如 CreateApple 或者 Create 生成的API動詞為 POST,對照表如下,若命中(忽略大小寫)對照表那麼該API的名稱中的這個動詞將會被省略,如 CreateApple 將會變成 Apple,如未在以下對照表中,將會使用預設動詞 POST

方法名開頭 動詞
create POST
add POST
post POST
get GET
find GET
fetch GET
query GET
update PUT
put PUT
delete DELETE
remove DELETE

(8)強烈建議方法名稱使用帕斯卡命名(PascalCase)規範,以更好的自動處理API名稱,且使用以上對照表的動詞。如:

新增蘋果 -> Add/AddApple/Create/CreateApple

更新蘋果 -> Update/UpdateApple

...

(9)[DynamicWebApi] 特性因為可被繼承,所以為了父類被誤識別,禁止放在除抽象類、介面以外的父類上。

2.配置

所有的配置均在物件 DynamicWebApiOptions 中,說明如下:

屬性名 是否必須 說明
DefaultHttpVerb 預設值:POST。預設HTTP動詞
DefaultAreaName 預設值:空。Area 路由名稱
DefaultApiPrefix 預設值:api。API路由字首
RemoveControllerPostfixes 預設值:AppService/ApplicationService。類名需要移除的字尾
RemoveActionPostfixes 預設值:Async。方法名需要移除的字尾
FormBodyBindingIgnoredTypes 預設值:IFormFile。不通過MVC繫結到引數列表的型別。

五.疑難解答

若遇到問題,可使用 Issues 進行提問。

六.結束

本專案開源地址:https://github.com/dotnetauth/Panda.DynamicWebApi 希望給個 Star 支援一下

本文Demo地址:XiaoChen.StudentManagement

ABP:https://github.com/aspnetboilerplate/aspnetboilerplate

相關文章