ASP.NET Core 中文文檔 第四章 MVC(2.3)格式化響應數據


原文:Formatting Response Data
作者:Steve Smith
翻譯:劉怡(AlexLEWIS)
校對:許登洋(Seay)

ASP.NET Core MVC 內建支持對相應數據(response data)的格式化,用來修正格式或生成客戶端指定的格式。

從 GitHub 上查看或下載相關樣例 。

特定格式的操作結果

某些操作結果(Action result)的類型是指定的特定格式,比如 JsonResult 或 ContentResult。Action 可以返回格式化為特定方式的具體結果。比如返回 JsonResult 將返回 JSON 格式化數據,而不是客戶端要求的的格式。同樣地,返回 ContentResult 將返回純文本格式的字符串數據(就像是簡單第返回一個字符串那樣)。

提示
Action 並不強制要求返回一個特定的類型,MVC 支持任何對象作為返回值。如果 Action 返回的是 IActionResult 的某個實現並且控制器繼承自 Controller ,那么開發人員就可以使用很多輔助方法(對應地就會有很多選擇)。從 Action 返回的結果如果不是 IActionResult 類型的對象的話將使用適當的 IOutputFormatter 實現序列化。

若要從繼承了 Controller 基類的控制器返回指定格式的數據,可以使用內置的輔助方法 Json
 來返回 JSON 格式,使用 Content 來返回純文本。此時你的 Action 方法的返回類型就必須是指定的結果類型(如 JsonResult)或 IActionResult

返回 JSON 格式的數據:

// GET: api/authors
[HttpGet]
public JsonResult Get()
{
    return Json(_authorRepository.List());
}

這個操作(Action)將返回的響應樣本:
json-response

注意,響應的內容類型是 application/json,它同時顯示在網絡請求列表中和響應的頭節點中。另外還要注意,由瀏覽器(比如 Microsoft Edge)提交的請求頭中 Accept 頭的選擇列表。當前的技術方案是忽略這個頭信息,下面將具體討論。

要返回純文本格式的數據,請使用 ContentResult 以及 Content 輔助方法:

// GET api/authors/about
[HttpGet("About")]
public ContentResult About()
{
    return Content("An API listing authors of docs.asp.net.");
}

該 Action 的響應:
text-response

注意在此情況下, Content-Type 將返回 test/plain。你也可以通過一個字符串來實現響應這一相同行為:

// GET api/authors/version
[HttpGet("version")]
public string Version()
{
    return "Version 1.0.0";
}

小技巧
對於將返回不同類型或選項的復雜操作(non-trivial actions)(比如根據操作的結果不同返回不同 HTTP 狀態碼),那么請使用 IActionResult 作為返回類型。

內容協商

內容協商(簡寫為 conneg)是指:當客戶端在 Accept 頭 中指定接受要求時會發生的過程。ASP.NET Core MVC 默認格式使用的是 JSON。內容協商由 ObjectResult 實現,它還內置了從輔助方法(它們盡數基於 ObjectResult)為指定的 Action 結果返回狀態碼的功能。你也可以返回一個模型類型(你自行定義的數據傳輸類),框架將自動為你將其包裝在 ObjectResult 內。

下面的 Action 方法使用 Ok 和 NotFound 兩種輔助方法:

// GET: api/authors/search?namelike=th
[HttpGet("Search")]
public IActionResult Search(string namelike)
{
    var result = _authorRepository.GetByNameSubstring(namelike);
    if (!result.Any())
    {
        return NotFound(namelike);
    }
    return Ok(result);
}

除非要求返回另一種服務器可以返回的格式,不然將返回 JSON 格式的響應。你可以使用像 Fiddler這樣的工具來創建包含 Accept 頭的、並且指定另一種格式的請求。在這種情況下,如果服務器具有創建請求中指定格式的 formatter,那么該結果將按客戶端所選的格式返回。
fiddler-composer

在上面的截圖中,Fiddler 的 Composer 標簽可用於生成請求,並指定 Accept: application/xml。默認情況下,ASP.NET Core MVC 只支持 JSON,所以即使是指定了另一種格式,返回的依舊是 JSON 格式。你可以在下一節中了解到如何增加其它格式。

控制器的 Action 可以返回 POCO(Plain Old CLR Objects),此時 ASP.NET Core MVC 會自動創建ObjectResult 並將該對象進行包裝。客戶端將獲得經序列化后的對象(默認是 JSON 格式,當然你也可以自己配置 XML 或其它格式)、如果對象返回的是 null,那么框架將返回 204 No Content 響應。

返回對象類型:

// GET api/authors/ardalis
[HttpGet("{alias}")]
public Author Get(string alias)
{
    return _authorRepository.GetByAlias(alias);
}

在本例中,有效的作者別名的請求將收到 200 OK 響應(帶着作者的數據),而無效別名將收到 204 No Content 響應。截圖中分別顯示了 XML 和 JSON 的響應。

內容協商過程

內容協商只在當 Accept 頭出現在請求中時才會發生。當請求包含 Accept 頭時,框架將按優先級的順序枚舉媒體類型(media types)並嘗試尋找能生產出 Accept 頭中指定格式(中的一種)的格式化程序。如果找到可以滿足客戶端請求的格式化程序,框架將嘗試尋找第一個能生產響應的格式化程序(除非開發人員已經在 MvcOptions 中配置返回 406 Not Acceptable 響應)。如果請求指定的格式是 XML,但 XML 格式化程序並沒有被配置,那么將使用 JSON 格式化程序。更通常地來講,如果沒有配置任何可以提供所請求的格式的格式化程序,那么將使用第一個格式化程序來格式化對象。如果沒有給定頭,那么所返回的對象將使用第一個格式化程序來序列化。在這種情況下並沒有發生內容協商——其實是由服務器來決定使用哪種格式來格式化對象。

提示
如果 Accept 頭中包含 /,那么 Header 將被忽略,除非 MvcOptions 的 RespectBrowserAcceptHeader 設置為 true。

瀏覽器和內容協商

與其它 API 客戶端不同,Web 瀏覽器一般都會在請求中包含 Accept 頭,其中使用通配符(wildcards)。默認情況下,當框架檢測到請求來自瀏覽器,它就會忽略 Accept 頭並返回應用程序配置的默認格式(如果沒有另行安排,則默認為 JSON 格式)。這樣一來,當使用不同的瀏覽器消費 API 時提供一致的體驗。

如果你希望你的應用程序優先考慮瀏覽器的 Accept 頭,你可以在 MVC 的配置中進行相關配置,具體來講是在 Startup.cs 的 ConfigureServices 方法中將 RespectBrowserAcceptHeader 設置為 true

services.AddMvc(options =>
{
  options.RespectBrowserAcceptHeader = true; // false by default
}

配置格式化程序

如果你的應用程序需要支持默認的 JSON 之外的格式,那么你需要在 project.json 文件中添加這些額外的依賴項,並配置 MVC 來支持它們。輸入和輸出的格式是可以隔離的。輸入格式通過使用模型綁定,輸出格式通過使用格式化響應。你也可以配置 自定義格式化

添加對 XML 格式的支持

為增加對 XML 格式的支持,需要在 project.json 的 dependencies 列表中增加“Microsoft.AspNetCore.Mvc.Formatters.Xml”包。

在 Startup.cs 的 MVC 配置中添加 XmlSerializerFormatters:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddXmlSerializerFormatters();

    services.AddScoped<IAuthorRepository, AuthorRepository>();
}

或者你可以只添加輸出格式:

services.AddMvc(options =>
{
  options.OutputFormatters.Add(new XmlSerializerOutputFormatter());
});

這兩種方法都將使用 System.Xml.Serialization.XmlSerializer 序列化結果。如果你願意,你可以通過添加其它相關格式來使用 System.Runtime.Serialization.DataContractSerializer 。

services.AddMvc(options =>
{
  options.OutputFormatters.Add(new XmlDataContractSerializerOutputFormatter());
});

一旦你添加了對 XML 格式的支持,你的控制器方法就可以根據 Accept 頭信息來返回相應的格式了,就像下面這個 Fiddler 的演示:
xml-response

你可以在 Inspectors 標簽下看到,原始 GET 請求設置了 Accept: application/xml 頭,在響應面板中顯示 Content-Type: application/xml 頭,並且有一個已經被序列化為 XML 格式的 Author 對象。

使用 Composer 面板可以修改請求,把 Accept 頭改為 application/json,然后執行這個請求,此時將獲得一個格式化為 JSON 格式的響應:
json-response-fiddler

在這個截圖中,你可以發現請求報文中的 Accept: application/json 頭以及與之相同的響應 Content-Type 。同時 Author 對象也序列化為 JSON 格式后出現在響應報文中。

強制特定格式化

若是你想為某個 action 限制響應格式,可以使用 [Produces] 過濾器。[Produces] 過濾器可以為這個控制器或 Action 指定響應格式。就像大多數 過濾器 那樣,這一過濾器可應用於 Action、控制器,甚至全局范圍。

[Produces("application/json")]
public class AuthorsController

[Produces] 過濾器會強制要求所有 AuthorsController 內的 Action 返回 JSON 格式的響應,即使其它格式化程序已經在應用程序中配置了、且客戶端也通過 Accept 頭指明要返回 JSON 之外的其他可用格式。更多請查閱 過濾器 ,包括如何在全局范圍應用過濾器。

特例格式化程序

一些特殊情況下使用的是內建的格式化實現。默認情況下,返回類型為 string 將格式化為 text/plain (如果通過 Accept 頭的話則是 text/html )。這種行為可以通過移除TextOutputFormatter 來改變。如果你如下例代碼這般在 Startup.cs 的 Configure 方法中移除了HttpNoContentOutputFormatter,那么當你某個返回類型為模型對象的 Action 返回了 null 時將返回 204 No Content 響應。下列代碼移除了 TextOutputFormatter 和 HttpNoContentOutputFormatter 。

services.AddMvc(options =>
{
  options.OutputFormatters.RemoveType<TextOutputFormatter>();
  options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
});

如果沒有 TextOutputFormatter,返回類型為 string 的操作將返回 406 Not Acceptable。此時請注意,如果存在 XML 格式化程序,此時將格式化該響應結果。

如果沒有 HttpNoContentOutputFormatter,空對象將使用配置的格式進行格式化。比如 JSON 格式將簡單的返回主體信息為 null 的響應,而 XML 格式將返回一個空的帶有 xsi:nil="true" 特性集的 XML 元素。

響應格式 URL 映射

客戶端可以在 URL 中請求特定的格式,比如在請求字符串(query string)或路徑(path)中,或者索性使用特定格式的文件擴展名(比如 .xml 或 .json)。從請求路徑中進行映射的話需要在 API 所使用的路由中進行指定,比如:

[FormatFilter]
public class ProductsController
{
  [Route("[controller]/[action]/{id}.{format?}")]
  public Product GetById(int id)

這一路由配置將允許使用可選的文件擴展名來指定格式。[FormatFilter] 特性將在 RouteData 中檢查該格式的值是否存在,並在創建響應時將響應數據映射為適當的格式。

路由 格式化
/products/GetById/5 默認輸出格式
/products/GetById/5.json JSON 格式(如果配置了的話)
/products/GetById/5.xml XML 格式(如果配置了的話)

返回目錄


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM