ASP.NET Core Controller 應該要使用 IActionResult 還是 ActionResult?

下面是一個非常簡單的 Controller ,他有一個 /auth/login 的 API 。在這個 method 中我們回傳的類型是 IActionResult:
    
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public IActionResult Login([FromBody] LoginDto dto)
    {
        var result = new TokenDto
        {
            Token = "aaa.bbb.ccc"
        };
        return Ok(result);
    }
}
    

在 Swagger UI 上面可以看到回應的範例,狀態代號為 200 ,代表成功

註: 這裡的 Swagger UI 是因為在大多數情況下在專案建立時已經自動安裝並啟用 Swashbuckle.AspNetCore 套件。

但是這個 API 在 200 的情況下是會回傳資料的,但是在上面的範例中我們無法直接的看出範例回應。
我們可以使用 ProducesResponseTypeAttribute 來標記回應的資料型態:
    
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    [ProducesResponseType(typeof(TokenDto), 200)]
    public IActionResult Login([FromBody] LoginDto dto)
    {
        var result = new TokenDto
        {
            Token = "aaa.bbb.ccc"
        };
        return Ok(result);
    }
}
    

回應的範例,狀態代號為 200 ,代表成功,會回傳自訂的 TokenDto 物件:

假設這個 API 的 Http Code 也有可能會回應 400,不帶有其他資訊,我們可以再加上一個 ProducesResponseTypeAttribute 來標記:
    
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    [ProducesResponseType(typeof(TokenDto), 200)]
    [ProducesResponseType(typeof(void), 400)]
    public IActionResult Login([FromBody] LoginDto dto)
    {
        var result = new TokenDto
        {
            Token = "aaa.bbb.ccc"
        };
        return Ok(result);
    }
}
    


假設我們使用 ActionResult 並標記資料型別:
    
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    public ActionResult<TokenDto> Login([FromBody] LoginDto dto)
    {
        var result = new TokenDto
        {
            Token = "aaa.bbb.ccc"
        };
        return Ok(result);
    }
}
    

會和手動使用 ProducesResponseTypeAttribute 標記 200 和回應資料型態的結果一樣,他可以很聰明的幫我們推斷:

但是假設我們需要標記除了 200 以外的回應,例如現在多加了一個 400 的回應:
    
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    [ProducesResponseType(typeof(void), 400)]
    public ActionResult<TokenDto> Login([FromBody] LoginDto dto)
    {
        var result = new TokenDto
        {
            Token = "aaa.bbb.ccc"
        };
        return Ok(result);
    }
}
    

結果 200 的回應和回應資料型態就不見了,剩下 400 手動補上的那個:

就算使用了 ActionResult ,如果想要顯示兩個或以上的回應格式,就需要手動加上 ProducesResponseTypeAttribute 並標記每一個才可以都顯示:
    
[ApiController]
[Route("auth")]
public class AuthController : ControllerBase
{
    [HttpPost("login")]
    [ProducesResponseType(typeof(TokenDto), 200)]
    [ProducesResponseType(typeof(void), 400)]
    public ActionResult<TokenDto> Login([FromBody] LoginDto dto)
    {
        var result = new TokenDto
        {
            Token = "aaa.bbb.ccc"
        };
        return Ok(result);
    }
}
    


結論

假設只想標記一個 API 回應格式,使用 ActionResult 並標記型態確實是一個好方式,在 Swagger UI 上面可以直接顯示,不需要多寫 ProducesResponseTypeAttribute。當有多個時再手動補充每一個即可。這時候也可以換使用 IActionResult ,因為都要每一個都標記了,所以使用 IActionResult 還比較好,才不會出現 ActionResult 標記的和 ProducesResponseTypeAttribute 200 的標記不同。

但是筆者喜歡在程式碼中保有一致的規範,因為 ActionResult 出現多個時還是要手動表標記,所以筆者在全部的 Controller method 中都會使用 IActionResult ,並手動替每一個 API 和每一種回應情況加上 ProducesResponseTypeAttribute。

當然,這只是筆者的做法,可以作為參考,但是在實際情況中還是要看團隊中 誰的拳頭大 的規範。

參考資料:
Microsoft.Learn - Controller action return types in ASP.NET Core web API

留言

張貼留言

如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com