asp.net core系列 38 WebAPI 返回類型與響應(yīng)格式--必備

一.返回類型

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):

操作返回類型

響應(yīng)格式化

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹦马,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子忆肾,更是在濱河造成了極大的恐慌荸频,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,657評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件客冈,死亡現(xiàn)場離奇詭異旭从,居然都是意外死亡,警方通過查閱死者的電腦和手機场仲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,889評論 3 394
  • 文/潘曉璐 我一進(jìn)店門和悦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人燎窘,你說我怎么就攤上這事摹闽√憧В” “怎么了褐健?”我有些...
    開封第一講書人閱讀 164,057評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長澜汤。 經(jīng)常有香客問我蚜迅,道長,這世上最難降的妖魔是什么俊抵? 我笑而不...
    開封第一講書人閱讀 58,509評論 1 293
  • 正文 為了忘掉前任谁不,我火速辦了婚禮,結(jié)果婚禮上徽诲,老公的妹妹穿的比我還像新娘刹帕。我一直安慰自己吵血,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,562評論 6 392
  • 文/花漫 我一把揭開白布偷溺。 她就那樣靜靜地躺著蹋辅,像睡著了一般。 火紅的嫁衣襯著肌膚如雪挫掏。 梳的紋絲不亂的頭發(fā)上侦另,一...
    開封第一講書人閱讀 51,443評論 1 302
  • 那天,我揣著相機與錄音尉共,去河邊找鬼褒傅。 笑死,一個胖子當(dāng)著我的面吹牛袄友,可吹牛的內(nèi)容都是我干的殿托。 我是一名探鬼主播,決...
    沈念sama閱讀 40,251評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼杠河,長吁一口氣:“原來是場噩夢啊……” “哼碌尔!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起券敌,我...
    開封第一講書人閱讀 39,129評論 0 276
  • 序言:老撾萬榮一對情侶失蹤唾戚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后待诅,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體叹坦,經(jīng)...
    沈念sama閱讀 45,561評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,779評論 3 335
  • 正文 我和宋清朗相戀三年卑雁,在試婚紗的時候發(fā)現(xiàn)自己被綠了募书。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,902評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡测蹲,死狀恐怖莹捡,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情扣甲,我是刑警寧澤篮赢,帶...
    沈念sama閱讀 35,621評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站琉挖,受9級特大地震影響启泣,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜示辈,卻給世界環(huán)境...
    茶點故事閱讀 41,220評論 3 328
  • 文/蒙蒙 一寥茫、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧矾麻,春花似錦纱耻、人聲如沸芭梯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,838評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽粥帚。三九已至,卻和暖如春限次,著一層夾襖步出監(jiān)牢的瞬間芒涡,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,971評論 1 269
  • 我被黑心中介騙來泰國打工卖漫, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留费尽,地道東北人。 一個月前我還...
    沈念sama閱讀 48,025評論 2 370
  • 正文 我出身青樓羊始,卻偏偏與公主長得像旱幼,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子突委,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,843評論 2 354

推薦閱讀更多精彩內(nèi)容