zoukankan      html  css  js  c++  java
  • 【译】在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格式化器(如果已配置)
  • 相关阅读:
    【JMeter_14】JMeter逻辑控制器__交替控制器<Interleave Controller>
    【JMeter_13】JMeter逻辑控制器__执行时间控制器<Runtime Controller>
    【JMeter_12】JMeter逻辑控制器__包括控制器<Include Controller>
    【JMeter_11】JMeter逻辑控制器__Switch控制器<Switch Controller>
    【JMeter_10】JMeter逻辑控制器__ForEach控制器<ForEach Controller>
    【JMeter_09】JMeter逻辑控制器__临界部分控制器<Critical Section Controller>
    【JMeter_08】JMeter逻辑控制器__While控制器<While Controller>
    【JMeter_07】JMeter逻辑控制器__循环控制器<Loop Controller>
    【JMeter_06】JMeter逻辑控制器__If控制器<If Controller>
    【JMeter_05】创建第一个简单的接口脚本
  • 原文地址:https://www.cnblogs.com/qianxingmu/p/13963720.html
Copyright © 2011-2022 走看看