關於 ActionResult 本篇將討論以下幾個問題
1. 關於 MVC 中的 ActionResult
2. ASP.NET Core web API 中的回傳型別
測試環境:
OS:Windows 10
IDE:Visual Studio 2019
1. 關於 MVC 中的 ActionResult
這邊指得是在 System.Web.Mvc
中的 ActionResult,而這跟 ASP.NET Core 中 IActionResult 的實作 ActionResult 有什麼關係呢?
舉例來說,大概就如同 iPhone 8 跟 iPhone 12 之間的關係,同樣都是 iPhone,但功能卻進化很多。從 Namespace 來看會清楚些,IActionResult 是 interface 它的 Namespace 是 Microsoft.AspNetCore.Mvc
,且 IActionResult 同名的實作 ActionResult 它的 Namespace 同樣是 Microsoft.AspNetCore.Mvc
。
在寫 MVC 時除了 ActionResult 之外最常見的大概就是繼承自 ActionResult 的System.Web.Mvc.JsonResult
,不過其實還有許多衍生類別如下 (MSDN)
System.Web.Mvc.ContentResult
System.Web.Mvc.EmptyResult
System.Web.Mvc.FileResult
System.Web.Mvc.HttpStatusCodeResult
System.Web.Mvc.JavaScriptResult
System.Web.Mvc.JsonResult
System.Web.Mvc.RedirectResult
System.Web.Mvc.RedirectToRouteResult
System.Web.Mvc.ViewResultBase
Microsoft.AspNetCore.Mvc
中的 ActionResult 衍生類別就更豐富了 (MSDN)
Microsoft.AspNetCore.Mvc.ChallengeResult
Microsoft.AspNetCore.Mvc.ContentResult
Microsoft.AspNetCore.Mvc.EmptyResult
Microsoft.AspNetCore.Mvc.FileResult
Microsoft.AspNetCore.Mvc.ForbidResult
Microsoft.AspNetCore.Mvc.JsonResult
Microsoft.AspNetCore.Mvc.LocalRedirectResult
Microsoft.AspNetCore.Mvc.ObjectResult
Microsoft.AspNetCore.Mvc.PartialViewResult
Microsoft.AspNetCore.Mvc.RedirectResult
Microsoft.AspNetCore.Mvc.RedirectToActionResult
Microsoft.AspNetCore.Mvc.RedirectToPageResult
Microsoft.AspNetCore.Mvc.RedirectToRouteResult
Microsoft.AspNetCore.Mvc.SignInResult
Microsoft.AspNetCore.Mvc.SignOutResult
Microsoft.AspNetCore.Mvc.StatusCodeResult
Microsoft.AspNetCore.Mvc.ViewComponentResult
Microsoft.AspNetCore.Mvc.ViewResult
Microsoft.AspNetCore.Mvc.RazorPages.PageResult
2. ASP.NET Core web API 中的回傳型別
ASP.NET Core web API Controller 有下面三種回傳型別
- Specific type
- IActionResult
- ActionResult<T>
Specific type
回傳特定類型(e.g. int、string、自訂義 class 等等),舉例來說,若要回傳 IEnumerable<WeatherForecast>
,則直接將回傳型別訂為 IEnumerable<WeatherForecast>
即可
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
在 ASP.NET Core 2.2 及之前的版本在回傳型別為 IEnumerable<T>
會有一些坑(細節請參考 MSDN)。
Specific type 若是要加上 Http Status Code,可以這樣寫
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
// 自訂 Http Status Code
Response.StatusCode = 400;
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
在 Http.HttpResponse
中可以看到 StatusCode
type 是 int,不過這樣的寫法實在很不人性化也很容易寫錯,建議不要這樣搞自己。
/// <summary>
/// Gets or sets the HTTP response code.
/// </summary>
public abstract int StatusCode { get; set; }
IActionResult
Microsoft.AspNetCore.Mvc.StatusCodeResult
繼承自 IActionResult 的實作 ActionResult,回傳時一併回傳 HTTPStatusCode
Class | HTTP Status Code |
---|---|
OkResult | 200 |
CreatedAtAction | 201 |
NoContentResult | 204 |
BadRequestResult | 400 |
UnauthorizedResult | 401 |
NotFoundResult | 404 |
ConflictResult | 409 |
UnsupportedMediaTypeResult | 415 |
UnprocessableEntityResult | 422 |
InternalServerErrorResult | 500 |
且在回傳時可以使用 ControllerBase
(MSDN)中的提供的方法來縮寫,如下例中的 NotFound()
(MSDN) & Ok(Object)
(MSDN)
[HttpGet("{id}")]
public IActionResult Get(int id)
{
if (id == 0)
{
// HTTP Status Code:404
// return new NotFoundResult();
return NotFound();
}
// HTTP Status Code:200
// return new OkObjectResult(product);
return Ok(Summaries);
}
可以在 Swagger 文件上顯示透過 Attribute [ProducesResponseType]
定義的 Http Status Code & 回傳型別,ASP.NET Core 2.2 之後還可以選擇使用 Web API Conventions。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(string[]))]
public IActionResult GetById(int id)
{
return Ok(Summaries);
}
ActionResult<T>
ASP.NET Core 2.1 之後開始有 ActionResult<T> 這個回傳型別,最大優點是可以同時支援回傳
- ActionResult 及其衍生類別
- Specific type
而能同時支援兩者則是因為 ActionResult<T> 中有實作轉換如下
/// <summary>
/// Implictly converts the specified <paramref name="value"/> to an <see cref="ActionResult{TValue}"/>.
/// </summary>
/// <param name="value">The value to convert.</param>
public static implicit operator ActionResult<TValue>(TValue value)
{
return new ActionResult<TValue>(value);
}
/// <summary>
/// Implictly converts the specified <paramref name="result"/> to an <see cref="ActionResult{TValue}"/>.
/// </summary>
/// <param name="result">The <see cref="ActionResult"/>.</param>
public static implicit operator ActionResult<TValue>(ActionResult result)
{
return new ActionResult<TValue>(result);
}
關於 ActionResult<T> 的優點還有
- 透過 Attribute
[ProducesResponseType]
定義 Http Status Code,回傳型別則可以透過泛型中的T
來取得 - return 也可以由
return Ok(Summaries);
簡化成return Summaries;
。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status200OK)]
public ActionResult<string[]> Get(int id)
{
return Summaries;
}
完整程式碼
GitHub:ActionResult
總結
稍微把 MSDN 上的內容做了整理,釐清 ActionResult 間的差異後覺得差異並不大,實際上使用還是依據需求或是團隊規範來寫就好,原則上還是統一一種寫法在維護上會比較方便。
參考資料
- MSDN_ActionResult_MVC
- MSDN_ActionResult_ASP.NET Core
- MSDN_Controller action return types in ASP.NET Core web API