過去 ASP.NET MVC 時代,我偏好用一般的 MVC Controller 寫 WebAPI,而非走 RESTful 風格的 ApiController。但 RESTful WebAPI 畢竟是當前主流,ASP.NET Core 的內建支援與緊密整合實在太香,於是,要在 ASP.NET Core 裡寫 WebAPI,我還是會選擇使用 WebAPI 範本及 ApiController。 ' 以 .NET 6 為例,來看看內建的 WebAPI Swagger 有多方便。用 dotnet new webapi -o WebApiDemo 建個全新 WebAPI 網站,裡面有個簡單的天氣預報 WebAPI 範例。由於專案已整合Swagger UI,執行網站輸入 /swagger 網址,我們可以從網頁看到所有 WebAPI 方法及參數資訊:

甚至可以從網頁介面輸入參數按執行看結果:

Swagger.json / OpenAPI 是公開標準,用 NSwag Studio 之類工具可自動產生呼叫端程式碼(不限 C#,其他語言平台也都找得到工具),我們只需專心寫好 WebAPI 邏輯,剩下工作都有現成介面與工具接手,叫人怎能捨得走出舒適圈?

不過,雖然選用 WebAPI 範本,在設計上我倒沒想轉向 RESTful 哲學 - 用 URI 指定資源、用 Get()、Post()、Put()、Delete() 方法對映動作。我仍然偏好在同一 Controller 用不同 Action 名稱定義多個方法的做法,覺得這樣比較簡單輕巧。
註:更詳細的理由可參考閒聊 - Web API 是否一定要 RESTful?一文,RESTful 與否見仁見智,若你偏好 RESTful 精神,文章後面可以跳過了。

不過,以 WebApi 專案範本中的 WeatherForecastController 為例,由於其宣告了 [ApiController] Attribute,預設要依循 RESTful 規則,一個 Controller 只能有一個 HTTP Get、一個 Post,若試著再加一個[HttpGet]方法(如以下的 GetNowString),將會得到 SwaggerGeneratorException: Conflicting method/path combination "GET WeatherForecast" for actions - WebApiDemo.Controllers.WeatherForecastController.GetNowString (WebApiDemo),WebApiDemo.Controllers.WeatherForecastController.Get (WebApiDemo). Actions require a unique method/path combination for Swagger/OpenAPI 3.0. Use ConflictingActionsResolver as a workaround 錯誤:

[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
    //...省略...
    [HttpGet]
    public string GetNowString() 
    {
        return DateTime.Now.ToString();
    }

    [HttpGet(Name = "GetWeatherForecast")]
    public IEnumerable<WeatherForecast> Get()
    {
        return Enumerable.Range(1, 5).Select(index => new WeatherForecast
        {
            Date = DateTime.Now.AddDays(index),
            TemperatureC = Random.Shared.Next(-20, 55),
            Summary = Summaries[Random.Shared.Next(Summaries.Length)]
        })
        .ToArray();
    }
}

所幸,這問題一點也不難解,只需加上一行 [Route("[action]")] 就可以囉。以下一併示範無參數,用 URL 路徑傳參數及 Query String 參數寫法:

    [HttpGet]
    [Route("[action]")]
    public string GetNowString() 
    {
        return DateTime.Now.ToString();
    }
    [HttpGet]
    [Route("[action]/{id}")]
    public string EchoId(int id) 
    {
        return id.ToString();
    }
    [HttpGet]
    [Route("[action]")]
    public string AddNumbers(int a, int b) 
    {
        return (a + b).ToString();
    }

成功!

感謝讀者 Chin Khai Hong 補充,路由設定亦可直接寫在 HttpGet 或 HttpPost,例如:HttpGet("[action]")。參考: Attribute routing with Http verb attributes

學會以上技巧,我們便能彈性擴充 ApiController,加入名稱不同的 HttpGet 或 HttpPost 方法,既享受 Swagger 整合,又不必受限於 RESTful 規則,兩全其美。

Tips of how to add multiple HttpGet or HttpPost actions in ASP.NET Core ApiController.


Comments

# by 小黑

感謝黑大~ 請教,WebApi 發行佈署後,能否有效的移除swagger 網頁?

# by Jeffrey

to 小黑,專案模版的 SwaggerUI 設定原本就只會在開發環境啟用 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); }

Post a comment


94 - 57 =