钱行慕

导航

【译】在ASP.NET Core Web API中格式化响应数据

原文链接:传送门

ASP.NET Core Web API支持格式化返回数据。返回数据可以以指定的格式进行格式化,或者对客户端请求的格式进行响应。

指定格式的Action结果

一些Action的结果类型会被限定于特定的格式,比如 JsonResult 和 ContentResult。Action可以返回以特定的格式进行格式化的结果,而不管客户端的参数选项。举个例子,返回JsonResult会返回基于JSON格式的数据。而返回ContentResult 或者string则会返回纯文本格式的字符串数据。

一个Action不要求返回指定的类型。ASP.NET Core支持任何对象返回值。如果一个Action的返回结果不是一个 IActionResult,那么这个返回结果便会用合适的IOutputFormatter 实现来进行序列化。更多信息,请参考Controller action return types in ASP.NET Core web API.。

内置的帮助器方法Ok 返回JSON格式的数据。

// GET: api/authors
[HttpGet]
public ActionResult Get()
{
    return Ok(_authors.List());
}

上述示例代码返回了一个作者的列表。使用F12浏览器开发者工具或者Postman来观察对上述Action的请求。我们可以看到:

  • 包含content-type: application/json; charset=utf-8的响应头信息被展示出来。
  • 请求头信息同样也被展示出来。比如Accept 头信息,而上述代码会忽略Accept 头信息。

为了返回纯文本格式的数据,请使用ContentResult 返回类型及 Content 帮助器方法。

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

在如上的代码中,返回的Content-Type 是 text/plain。返回一个字符串提供的Content-Type为  text/plain:。

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

对于具有多个返回类型的Action,我们可以返回一个IActionResult。比如,基于执行的操作的结果返回不同的HTTP状态码的情况。

内容协商

当客户端指定了一个Accept header时便会发生内容协商。ASP.NET Core默认使用的格式是JSON。内容协商是:

  • 被 ObjectResult 实现
  • 内置于从帮助器方法返回的指定状态码的Action结果中。Action结果帮助器方法是基于ObjectResult 的

当返回一个模型类型时,其返回结果便是ObjectResult。

如下的Action方法使用Ok及NotFound帮助器方法。

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

默认的,ASP.NET Core支持 application/json,text/json,text/plain 媒体类型。例如Fiddler 或者 Postman这样的工具可以设置Accept请求头以指定返回格式。当Accept 头信息包含服务器支持的类型时,便会返回那个类型。下一章节将会展示给你如何添加额外的格式化器。

控制器Action可以返回一个POCO。当返回一个POCO时,运行时会自动创建一个封装了此对象的ObjectResult。客户端得到了格式化后的序列化对象。如果返回的对象是null,便会返回一个204 No Content 响应。

如下代码返回一个对象类型;

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

在如上代码中,请求一个有效的作者别名会返回一个200 OK响应以及作者数据。请求一个无效的别名将返回一个204 No Content 响应。

Accept 头信息

当Accept头信息出现在一个请求中时,便会发生内容协商。当一个请求包含一个accept头信息时,ASP.NET Core便会:

  • 以特定的顺序枚举accept头中的媒体类型
  • 以指定的格式之一,尝试找到一个可以产生响应的格式化器

如果没有找到格式化器来满足客户端的请求,ASP.NET Core将会:

  • 返回406 Not Acceptable,如果设置了MvcOptions,则返回 -
  • 尝试找到可以生成响应的第一个格式化器

如果对于请求的格式没有配置相应的格式化器,便会使用可以格式化对象的第一个格式化器。如果请求中没有出现Accept 头信息:

  • 将会使用可以处理此对象的第一个格式化器来序列化响应
  • 不会发生内容协商。服务器会决定返回何种格式

如果Accept头包含*/*,便会忽略头信息,除非在MvcOptions 中 RespectBrowserAcceptHeader 属性被设置为 true。

浏览器和内容协商

与典型的API客户端不同的是,Web浏览器支持Accept头。Web浏览器会指定许多格式,包括wildcards。默认的,当框架检测出来一个请求是来自浏览器时,其会:

  • 忽略Accept 头
  • 将以JSON格式返回内容,除非配置了其他选项

当消费API时,这提供了更加一致的跨浏览器体验。

为了配置一个App来支持浏览器Accept头,请将RespectBrowserAcceptHeader 设置为 true。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        options.RespectBrowserAcceptHeader = true; // false by default
    });
}

配置格式化器

需要支持其他格式的app可以添加合适的Nuget包并进行配置使其支持相应的格式。输入内容和输出内容的格式化器是分开的。输入格式化器被Model Binding 来使用。输出格式化器用来格式化响应。关于创建自定义格式化器的信息,请参考 Custom Formatters

添加XML格式支持

使用 XmlSerializer 实现的XML格式化器可以通过调用AddXmlSerializerFormatters 来进行配置。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddXmlSerializerFormatters();
}

如上的代码使用XmlSerializer来序列化结果。

当使用如上的代码时,控制器Action将基于请求的Accept头来返回合适的格式。

配置基于System.Text.Json的格式化器

基于System.Text.Json的格式化器特性可通过使用Microsoft.AspNetCore.Mvc.JsonOptions.SerializerOptions来进行配置。

services.AddControllers().AddJsonOptions(options =>
{
    // Use the default property (Pascal) casing.
    options.JsonSerializerOptions.PropertyNamingPolicy = null;

    // Configure a custom converter.
    options.JsonSerializerOptions.Converters.Add(new MyCustomJsonConverter());
});

基于一个单独Action基元的输出序列化选项,可以使用JsonResult进行配置,比如:

public IActionResult Get()
{
    return Json(model, new JsonSerializerOptions
    {
        WriteIndented = true,
    });
}

在ASP.NET Core 3.0之前,默认下使用基于Newtonsoft.Json包的JSON格式化器。在ASP.NET Core 3.0及之后的版本中,默认的JSON格式化器是基于System.Text.Json的。通过安装 Microsoft.AspNetCore.Mvc.NewtonsoftJson 并在Startup.ConfigureServices中对其进行配置,我们仍然可以支持基于Newtonsoft.Json的格式化器。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers()
        .AddNewtonsoftJson();
}

在如上的代码中,对于AddNewtonsoftJson 的调用将配置如下特性:Web API, MVC, and Razor Pages 来使用Newtonsoft.Json。

某些特性可能不能与基于System.Text.Json的格式化器工作良好,其需要一个基于Newtonsoft.Json的格式化器。如果app具有如下几种情况,请继续使用基于Newtonsoft.Json的格式化器:

  • 使用了Newtonsoft.Json属性,比如[JsonProperty] 或者 [JsonIgnore]
  • 自定义了序列化设置
  • 依赖于Newtonsoft.Json提供的特性
  • 配置了Microsoft.AspNetCore.Mvc.JsonResult.SerializerSettings。在ASP.NET Core 3.0以前,JsonResult.SerializerSettings接收一个JsonSerializerSettings 的实例,其被指定为Newtonsoft.Json。
  • 生成一个 OpenAPI 文档

基于Newtonsoft.Json的格式化器特性可以通过Microsoft.AspNetCore.Mvc.MvcNewtonsoftJsonOptions.SerializerSettings来进行配置。

services.AddControllers().AddNewtonsoftJson(options =>
{
    // Use the default property (Pascal) casing
    options.SerializerSettings.ContractResolver = new DefaultContractResolver();

    // Configure a custom converter
    options.SerializerSettings.Converters.Add(new MyCustomJsonConverter());
});

基于一个Action基元的输出序列化选择可以用JsonResult进行配置,比如:

public IActionResult Get()
{
    return Json(model, new JsonSerializerSettings
    {
        Formatting = Formatting.Indented,
    });
}

指定一个格式

为了限定响应格式,可以应用 [Produces] 过滤器。如同大部分过滤器一样,[Priduces]可用于控制器,Action,以及全局域中。

[ApiController]
[Route("[controller]")]
[Produces("application/json")]
public class WeatherForecastController : ControllerBase
{

上述 [Produces]过滤器产生了如下效果:

  • 使得控制器内的所有Action均返回基于JSON格式的响应
  • 如果配置了其他格式化器并且客户端指定了一个不同的格式,那么也会返回JSON

更多信息,请参考 Filters

特殊例子格式化器

一些特殊的场景使用内置的格式化器来实现。默认情况下,string返回类型被格式化为一个text/plain (如果通过Accept 来请求,则格式化为 text/html)。通过移除 StringOutputFormatter可以删除此行为。可以在ConfigureServices 方法中移除格式化器。返回一个对象的Action在返回null时候其会返回一个204 No Content。通过移除 HttpNoContentOutputFormatter 可删除此行为。如下代码移除了StringOutputFormatter 和 HttpNoContentOutputFormatter。

public void ConfigureServices(IServiceCollection services)
{
    services.AddControllers(options =>
    {
        // requires using Microsoft.AspNetCore.Mvc.Formatters;
        options.OutputFormatters.RemoveType<StringOutputFormatter>();
        options.OutputFormatters.RemoveType<HttpNoContentOutputFormatter>();
    });
}

如果没有StringOutputFormatter,内置的JSON格式化器会格式化string返回类型。如果内置的JSON格式化器被移除并且有一个XML格式化器是可用的,那么XML格式化器将会格式化string返回类型。否则,string返回类型返回一个406 Not Acceptable。

如果没有HttpNoContentOutputFormatter,将使用配置的格式化器来格式化null对象。比如:

  • JSON格式化器会返回一个响应体为null的响应
  • XML格式化器会返回一个空的XML元素,并设置了属性:xsi:nil="true"

响应格式URL映射

客户端可以作为URL的一部分来请求一个特殊的格式。比如:

  • 在查询字符串或者路径的一部分
  • 通过使用一个指定格式的文件扩展名,比如.xml or .json

来自于请求路径的映射应该指定在API使用的路由中。比如:

[Route("api/[controller]")]
[ApiController]
[FormatFilter]
public class ProductsController : ControllerBase
{
    [HttpGet("{id}.{format?}")]
    public Product Get(int id)
    {

上述路由将请求的格式指定为可选的文件扩展名。[FormatFilter] 特性将会检查RouteData中格式值的存在,并在创建响应时,将响应格式映射为合适的格式化器。

路由格式化器
/api/products/5 默认的输出格式化器
/api/products/5.json JSON格式化器(如果已配置)
/api/products/5.xml XML格式化器(如果已配置)

posted on 2020-11-12 14:26  钱行慕  阅读(674)  评论(0编辑  收藏  举报