一.返回類型
ASP.NET Core 提供以下 Web API Action方法返回類型選項僻爽,以及說明每種返回類型的最佳適用情況
(1) 固定類型
(2) IActionResult
(3) ActionResult<T>
1.1 固定類型
最簡單的操作是返回基元或復(fù)雜數(shù)據(jù)類型(如 string 或自定義對象類型)虫几。 請參考以下Action,該Action返回自定義 Product 對象的集合:
[HttpGet]
public IEnumerable<Product> Get()
{
return _repository.GetProducts();
}
適用場景:在執(zhí)行Action期間均芽,無需要根據(jù)條件判斷返回不同類型怯邪,只返回固定類型即可滿足要求蝉娜。 上述操作不接受任何參數(shù),因此不需要參數(shù)約束驗證哥纫。
1.2 IActionResult類型
當(dāng)Action方法中可能有多個 ActionResult 返回類型時霉旗,適合使用 IActionResult 返回類型。ActionResult 類型可以表示多種 HTTP 狀態(tài)代碼。 屬于此類別的一些常見返回類型包括:BadRequestResult (400)厌秒、NotFoundResult (404) 和 OkObjectResult (200)读拆。
由于Action方法中有多個返回類型和路徑,因此必須使用 [ProducesResponseType] 特性鸵闪。 此特性可針對 Swagger 等工具生成的 API 幫助頁生成更多描述性響應(yīng)詳細(xì)信息(上篇有介紹)檐晕。 [ProducesResponseType] 指示Action將返回的已知類型和 HTTP 狀態(tài)代碼。
下面是一個同步Action蚌讼,該Action方法中可能有兩種返回類型:
[HttpGet("{id}")]
[ProducesResponseType(typeof(Product), StatusCodes.Status200OK)]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public IActionResult GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
return Ok(product);
}
下面是一個異步Action辟灰,該Action方法中可能有兩種返回類型:
[HttpPost]
[ProducesResponseType(typeof(Product), StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<IActionResult> CreateAsync([FromBody] Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
適用場景:當(dāng)Action方法中可能有多個 ActionResult 返回類型時,適合使用 IActionResult 返回類型篡石。
1.3 ActionResult<T>
ASP.NET Core 2.1 引入了 ActionResult<T> 返回類型芥喇。 它支持返回從 ActionResult 派生的類型或返回固定類型。ActionResult<T> 提供以下優(yōu)勢:
(1) 簡化ProducesResponseType 例如:[ProducesResponseType(200, Type = typeof(Product))] 可簡化為 [ProducesResponseType(200)]
(2) 隱式強制轉(zhuǎn)換運算符凰萨,將 T 轉(zhuǎn)換為 ObjectResult继控,也就是將 return new ObjectResult(T); 簡化為 return T;
下面是同步示例,(1)簡化ProducesResponseType胖眷,(2)返回隱式轉(zhuǎn)換武通。
[HttpGet("{id}")]
[ProducesResponseType(StatusCodes.Status404NotFound)]
public ActionResult<Product> GetById(int id)
{
if (!_repository.TryGetProduct(id, out var product))
{
return NotFound();
}
// return new ObjectResult(product);
return product;
}
下面是異步示例:
[HttpPost]
[ProducesResponseType(StatusCodes.Status201Created)]
[ProducesResponseType(StatusCodes.Status400BadRequest)]
public async Task<ActionResult<Product>> CreateAsync(Product product)
{
if (product.Description.Contains("XYZ Widget"))
{
return BadRequest();
}
await _repository.AddProductAsync(product);
return CreatedAtAction(nameof(GetById), new { id = product.Id }, product);
}
適用場景:對比IActionResult類型適用場景,它提供了二種優(yōu)勢瘦材。
最后建議:不要用特定類型返回。 對于有返回類型的使用ActionResult<T>,相反對于沒有返回類型的使用IActionResult仿畸。 二者使用在“ asp.net core系列 36 WebAPI 搭建詳細(xì)示例”中有介紹食棕。
二.響應(yīng)數(shù)據(jù)的格式化
響應(yīng)數(shù)據(jù)是:response返回到客戶端的數(shù)據(jù)。在ASP.NET Core MVC 中错沽,包含對固定格式(json,xml,string..)或根據(jù)客戶端規(guī)范(Accept)來設(shè)置響應(yīng)數(shù)據(jù)格式的內(nèi)置支持簿晓。默認(rèn)返回json數(shù)據(jù)格式。
2.1 設(shè)置固定格式的action結(jié)果
對于返回固定格式千埃,例如返回JsonResult 和 ContentResult憔儿。這樣api向客戶端始終返回固定的格式,不考慮客戶端的Accept選項設(shè)置放可。JsonResult 始終返回josn數(shù)據(jù)格式, ContentResult始終返回純文本數(shù)據(jù)格式谒臼。如果不需要Action返回固定數(shù)據(jù)格式,可以返回IActionResult 耀里,這樣可以有多種選擇的數(shù)據(jù)格式蜈缤。默認(rèn)是json數(shù)據(jù)格式。
(1) 返回json格式的數(shù)據(jù),使用fiddler請求url冯挎,返回客戶端json格式數(shù)據(jù)底哥,如下圖:
/// <summary>
/// 返回固定的json格式字符串
/// </summary>
/// <returns></returns>
[HttpGet("Get")]
public JsonResult Get()
{
return Json(_context.TodoItems.ToList());
}
(2) 返回純文本格式數(shù)據(jù),使用fiddler請求url,返回客戶端字符串趾徽,如下圖
/// <summary>
/// 返回固定的字符串格式
/// </summary>
/// <returns></returns>
[HttpGet("Message")]
public ContentResult Message()
{
return Content("hello");
}
2.2 返回格式協(xié)商
在公開的api的場景续滋,請求方(客戶端)在獲取數(shù)據(jù)時,他們可能要求返回自己想要的數(shù)據(jù)格式孵奶。這樣就不能使用固定的數(shù)據(jù)格式(一般也不推薦)疲酌。當(dāng)客戶端指定 Accept 標(biāo)頭時,就可以實現(xiàn)內(nèi)容協(xié)商拒课,對于內(nèi)容協(xié)商返回數(shù)據(jù)格式由 ObjectResult 實現(xiàn) 徐勃。
下面的案例中,返回IActionResult早像,返回的數(shù)據(jù)格式僻肖,由ObjectResult 來確定,默認(rèn)是json數(shù)據(jù)格式卢鹦。
[HttpGet("{id}", Name = "GetTodoItem")]
public async Task<ActionResult<TodoItem>> GetTodoItem(long id)
{
var todoItem = await _context.TodoItems.FindAsync(id);
if (todoItem == null)
{
//返回狀態(tài)碼404臀脏,打包到了ObjectResult中
return NotFound();
}
//返回實體,打包到了ObjectResult中
return todoItem;
}
客戶端通過指定Accept: application/xml冀自,希望返回xml數(shù)據(jù)格式揉稚,但還是json數(shù)據(jù)格式(見下圖)。這是因為:
(1) 默認(rèn)情況下熬粗,當(dāng)框架檢測到請求來自瀏覽器時搀玖,它將忽略 Accept 標(biāo)頭轉(zhuǎn)而以應(yīng)用程序的配置默認(rèn)格式。
(2) 如果請求指定 XML驻呐,但是未配置 XML 格式化程序灌诅,那么將使用 JSON 格式化程序。
如果應(yīng)用程序要服從瀏覽器 accept 標(biāo)頭含末,可以將此配置為 MVC 配置的一部分猜拾,方法是在 Startup.cs 中以 ConfigureServices 方法將 RespectBrowserAcceptHeader 設(shè)置為 true,并設(shè)置以客戶端格式優(yōu)先佣盒。
services.AddMvc(options =>
{
//優(yōu)先客戶端指定數(shù)據(jù)格式
options.RespectBrowserAcceptHeader = true;
//添加xml數(shù)據(jù)格式的輸出
options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});
如下圖所示挎袜,客戶端指定Accept: application/xml,服務(wù)端就返回了xml數(shù)據(jù)格式肥惭,同樣指定Accept: application/json 盯仪,服務(wù)端就返回json數(shù)據(jù)格式。
2.3 強制執(zhí)行固定格式
如果需要限制固定Action的響應(yīng)格式蜜葱,那么可以應(yīng)用 [Produces] 篩選器磨总。 [Produces] 篩選器指定特定action(或控制器)的響應(yīng)格式。 如同大多篩選器笼沥,這可以在action層面蚪燕、控制器層面或全局范圍內(nèi)應(yīng)用娶牌。 這樣格式協(xié)商就失敗,始終返回json數(shù)據(jù)格式馆纳。
//控制器層面強制使用json格式
[Produces("application/json")]
[ApiController]//添加特性诗良,代表是一個Web API控制器
public class TodoController : Controller
//action層面強制返回json格式
[Produces("application/json")]
[HttpGet]
public async Task<ActionResult<IEnumerable<TodoItem>>> GetTodoItems()
{
//using Microsoft.EntityFrameworkCore;
return await _context.TodoItems.ToListAsync();
}
2.4 特殊情況格式化程序
如果要過濾客戶端Accept請求的某些類型,例如過濾text/plain鲁驶。string 默認(rèn)是text/plain類型鉴裹,如果刪除TextOutputFormatter,則string返回類型 是406 Not Acceptable钥弯。
//下面對返回string字符串或返回http 204的進(jìn)行過濾,代碼對應(yīng)如下:
services.AddMvc(options =>
{
options.OutputFormatters.RemoveType<TextOutputFormatter>();
options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
});
2.5 響應(yīng)格式URL映射
當(dāng)格式協(xié)商配置好了以后径荔,客戶端可以請求特定格式作為URL的一部分。下面是Url映射的配置示例脆霎。
[FormatFilter]
public class ProductsController
{
[Route("[controller]/[action]/{id}.{format?}")]
public Product GetById(int id)
當(dāng)客戶端訪問該url总处,返回默認(rèn)數(shù)據(jù)格式:
/products/GetById/5
當(dāng)客戶端訪問該url,返回json數(shù)據(jù)格式:
/products/GetById/5.json
當(dāng)客戶端訪問該url睛蛛,返回xml數(shù)據(jù)格式:
/products/GetById/5.xml
參考文獻(xiàn):