
控制 ASP.NET Core 响应的格式,并了解如何通过格式化程序的帮助或直接从操作返回带有自定义状态代码的 JSON 结果。
创建 HTTP API 时,您希望能够控制如何响应请求,包括更改状态代码和正文格式。在这篇博文中,我们将研究如何通过在 ASP.NET Core 中自定义状态代码来控制 JSON 响应。
ASP.NET Core 如何决定响应格式
首先让我们看一下 ASP.NET Core 实际上是如何生成 JSON 结果的,这将有助于我们理解响应格式背后的机制。当我们使用以下 dotnet CLI 命令使用 Web API 配置构建 ASP.NET Core 项目时:
dotnet new webapi --no-https --auth=None
我们的 startup.cs 文件如下所示:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
}
我们还有一个名为的示例控制器文件WeatherForecastController.cs,其形状如下:
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet]
public IEnumerable<WeatherForecast> Get()
{
var rng = new Random();
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
}
}
这些是我们可以借鉴的基础知识,以便我们了解响应在格式和状态代码方面是如何形成的。
当我们使用 配置 ASP.NET Core 时services.AddControllers,这会添加内置的OutputFormatters,用于将对象写入输出流:
- Microsoft.AspNetCore.Mvc.Formatters.HttpNoContentOutputFormatter
- Microsoft.AspNetCore.Mvc.Formatters.StringOutputFormatter
- Microsoft.AspNetCore.Mvc.Formatters.StreamOutputFormatter
- Microsoft.AspNetCore.Mvc.Formatters.SystemTextJsonOutputFormatter
这些输出格式化程序允许操作返回任何对象返回值。此处通过称为内容协商的过程选择格式化程序,该过程在客户端指定 Accept 标头时发生。
让我们通过一个例子来看一下。通过以下修改,我们将 XML 输出格式化程序添加到支持的格式化程序列表中:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers().AddXmlSerializerFormatters();
}
现在,我们将看到,当我们指定想要通过 Accept 标头以 XML 形式接收响应主体时,响应将以 XML 形式编写:
curl -v -H "Accept: application/xml" http://localhost:5000/WeatherForecast
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET /WeatherForecast HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: application/xml
>
< HTTP/1.1 200 OK
< Date: Mon, 13 Jan 2020 21:06:32 GMT
< Content-Type: application/xml; charset=utf-8
< Server: Kestrel
< Content-Length: 829
<
* Connection #0 to host localhost left intact
<ArrayOfWeatherForecast xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><WeatherForecast><Date>2020-01-14T21:06:32.786281+00:00</Date><TemperatureC>3</TemperatureC><Summary>Hot</Summary></WeatherForecast><WeatherForecast><Date>2020-01-15T21:06:32.794902+00:00</Date><TemperatureC>36</TemperatureC><Summary>Mild</Summary></WeatherForecast><WeatherForecast><Date>2020-01-16T21:06:32.794907+00:00</Date><TemperatureC>-14</TemperatureC><Summary>Scorching</Summary></WeatherForecast><WeatherForecast><Date>2020-01-17T21:06:32.794908+00:00</Date><TemperatureC>5</TemperatureC><Summary>Chilly</Summary></WeatherForecast><WeatherForecast><Date>2020-01-18T21:06:32.794908+00:00</Date><TemperatureC>-13</TemperatureC><Summary>Scorching</Summary></WeatherForecast></ArrayOfWeatherForecast>
当我们将 Accept 标头指定为 application/json 时,响应将采用 JSON 格式:
curl -v -H "Accept: application/json" http://localhost:5000/WeatherForecast
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET /WeatherForecast HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: application/json
>
< HTTP/1.1 200 OK
< Date: Mon, 13 Jan 2020 21:09:05 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
[{"date":"2020-01-14T21:09:05.635107+00:00","temperatureC":4,"temperatureF":39,"summary":"Hot"},{"date":"2020-01-15T21:09:05.635279+00:00","temperatureC":-8,"temperatureF":18,"summary":"Scorching"},{"date":"2020-01-16T21:09:05.635281+00:00","temperatureC":42,"temperatureF":107,"summary":"Balmy"},{"date":"2020-01-17T21:09:05.635281+00:00","temperatureC":27,"temperatureF":80,"summary":"Chilly"},{"date":"2020-01-18T21:09:05.635282+00:00","temperatureC":44,"temperatureF":111,"summary":"Warm"}]%
除了返回 POCO(普通旧 CLR 对象)并让内容协商决定选择哪种输出格式化程序之外,您还可以从控制器操作返回 IActionResult ,它定义了表示操作方法结果的契约,这可以让您直接控制返回类型。例如,内置助手 IActionResult 实现JsonResult返回 JSON 格式的数据,而不管 Accept 标头如何。
[HttpGet]
public IActionResult Get()
{
var rng = new Random();
var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
return new JsonResult(result);
}
curl -v -H "Accept: application/xml" http://localhost:5000/WeatherForecast
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET /WeatherForecast HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: application/xml
>
< HTTP/1.1 200 OK
< Date: Mon, 13 Jan 2020 21:18:39 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
[{"date":"2020-01-14T21:18:40.678848+00:00","temperatureC":12,"temperatureF":53,"summary":"Scorching"},{"date":"2020-01-15T21:18:40.685278+00:00","temperatureC":23,"temperatureF":73,"summary":"Chilly"},{"date":"2020-01-16T21:18:40.685283+00:00","temperatureC":24,"temperatureF":75,"summary":"Cool"},{"date":"2020-01-17T21:18:40.685284+00:00","temperatureC":-10,"temperatureF":15,"summary":"Hot"},{"date":"2020-01-18T21:18:40.685284+00:00","temperatureC":-5,"temperatureF":24,"summary":"Bracing"}]%
更改响应状态代码
现在,让我们看看如何更改响应状态代码。对于上面提到的所有情况,我们可以Response.StatusCode在返回结果之前简单地设置为我们想要响应的适当状态代码:
[HttpGet]
public IActionResult Get()
{
var rng = new Random();
var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
Response.StatusCode = StatusCodes.Status400BadRequest;
return new JsonResult(result);
}
您可以在下面的请求示例中看到我们收到了 400 状态代码的响应:
curl -v -H "Accept: application/json" http://localhost:5000/WeatherForecast
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET /WeatherForecast HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: application/json
>
< HTTP/1.1 400 Bad Request
< Date: Mon, 13 Jan 2020 21:34:53 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
[{"date":"2020-01-14T21:34:54.415857+00:00","temperatureC":21,"temperatureF":69,"summary":"Sweltering"},{"date":"2020-01-15T21:34:54.4239+00:00","temperatureC":-6,"temperatureF":22,"summary":"Freezing"},{"date":"2020-01-16T21:34:54.423907+00:00","temperatureC":9,"temperatureF":48,"summary":"Mild"},{"date":"2020-01-17T21:34:54.423907+00:00","temperatureC":51,"temperatureF":123,"summary":"Freezing"},{"date":"2020-01-18T21:34:54.423908+00:00","temperatureC":49,"temperatureF":120,"summary":"Hot"}]%
除了这种设置状态代码的简单方法之外,ControllerBase 对象上还有一些辅助方法,使我们能够塑造响应。例如, Conflict 方法创建一个 ConflictObjectResult,该结果会产生Status409Conflict响应。
[HttpGet]
public IActionResult Get()
{
var rng = new Random();
var result = Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = rng.Next(-20, 55),
Summary = Summaries[rng.Next(Summaries.Length)]
})
.ToArray();
return Conflict(result);
}
curl -v -H "Accept: application/json" http://localhost:5000/WeatherForecast
* Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5000 (#0)
> GET /WeatherForecast HTTP/1.1
> Host: localhost:5000
> User-Agent: curl/7.54.0
> Accept: application/json
>
< HTTP/1.1 409 Conflict
< Date: Mon, 13 Jan 2020 21:38:03 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Transfer-Encoding: chunked
<
* Connection #0 to host localhost left intact
[{"date":"2020-01-14T21:38:04.665259+00:00","temperatureC":-19,"temperatureF":-2,"summary":"Chilly"},{"date":"2020-01-15T21:38:04.673328+00:00","temperatureC":47,"temperatureF":116,"summary":"Bracing"},{"date":"2020-01-16T21:38:04.673334+00:00","temperatureC":28,"temperatureF":82,"summary":"Chilly"},{"date":"2020-01-17T21:38:04.673335+00:00","temperatureC":17,"temperatureF":62,"summary":"Warm"},{"date":"2020-01-18T21:38:04.673335+00:00","temperatureC":49,"temperatureF":120,"summary":"Bracing"}]%
在这种情况下,无论我们Response.StatusCode在操作中设置什么,它都会被Conflict辅助方法覆盖。
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

被折叠的 条评论
为什么被折叠?



