简体   繁体   English

如何在 ASP.NET Core 3.1 中为 HTTP 状态 415 创建自定义响应?

[英]How to create a custom response for HTTP status 415 in ASP.NET Core 3.1?

I'm creating a ASP.NET Core ( .NET Core 3.1 ) API that accepts requests with JSON content.我正在创建一个 ASP.NET Core ( .NET Core 3.1 ) API 接受内容为 JSON 的请求。

If I POST to my endpoint non-JSON request content (while running in debug in VS with IIS Express) I get the response similar to:如果我 POST 到我的端点非 JSON 请求内容(同时在 VS 中使用 IIS Express 调试运行)我得到类似于以下的响应:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
    "title": "Unsupported Media Type",
    "status": 415,
    "traceId": "|a53c33b7-43fa646efa5c6ab9."
}

Instead of this being sent out in the response I would like to create my own response for this scenario.我不想在响应中发送这个,而是想为这种情况创建我自己的响应。 I would like to do this by changing the pipeline if possible.如果可能的话,我想通过改变管道来做到这一点。

Within my Startup.cs I added:在我的Startup.cs ,我添加了:

public void Configure(IApplicationBuilder app)
{
    app.Use(async (context, next) =>
    {
        await next.Invoke();

        if (context.Response.StatusCode == StatusCodes.Status415UnsupportedMediaType)
        {
            await context.Response.WriteAsync("Unsupported media type handled");
        }
    });
    
    // other calls on app
}

However, upon running the app and hitting my endpoint I now get:但是,在运行该应用程序并到达我的端点后,我现在得到:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",
    "title": "Unsupported Media Type",
    "status": 415,
    "traceId": "|3ec437d7-4b20f64c28d04f16."
}Unsupported media type handled

I've tried calling context.Response.Clear();我试过调用context.Response.Clear(); before I write my new text, but this just outputs the JSON above without my custom text.在我写我的新文本之前,但这只是输出上面的 JSON 而没有我的自定义文本。 Which seems even stranger.这似乎更奇怪。

What is going on here?这里发生了什么? It feels like something is handling and creating the 415 response before my pipeline even starts.在我的管道开始之前,感觉好像有什么东西正在处理和创建 415 响应。 Or what might I be doing wrong?或者我可能做错了什么?

I highly encourage you to reconsider your initial idea to replace the response content you are getting from ASP.NET core with your own custom response content.我强烈建议您重新考虑您最初的想法,即用您自己的自定义响应内容替换您从 ASP.NET 核心获得的响应内容。

ASP.NET core is opinionated about API controllers error handling. ASP.NET 核心对 API 控制器的错误处理有意见。 They have decided to handle some client errors (for instance the 415 status code) by using the Problem Details specification .他们决定使用问题详细信息规范来处理一些客户端错误(例如 415 状态代码)。

Quotes from the Problem Details RFC :问题详细信息 RFC中的引述:

This document defines a "problem detail" as a way to carry machine- readable details of errors in a HTTP response to avoid the need to define new error response formats for HTTP APIs.本文档将“问题详细信息”定义为一种在 HTTP 响应中携带机器可读错误详细信息的方法,以避免需要为 HTTP API 定义新的错误响应格式。

HTTP [RFC7230] status codes are sometimes not sufficient to convey enough information about an error to be helpful. HTTP [RFC7230] 状态代码有时不足以传达有关错误的足够信息以提供帮助。 While humans behind Web browsers can be informed about the nature of the problem with an HTML [W3C.REC-html5-20141028] response body, non-human consumers of so-called "HTTP APIs" are usually not.虽然 Web 浏览器背后的人类可以通过 HTML [W3C.REC-html5-20141028] 响应主体了解问题的性质,但所谓的“HTTP API”的非人类消费者通常不会。

This specification defines simple JSON [RFC7159] and XML [W3C.REC-xml-20081126] document formats to suit this purpose.本规范定义了简单的 JSON [RFC7159] 和 XML [W3C.REC-xml-20081126] 文档格式来满足此目的。 They are designed to be reused by HTTP APIs, which can identify distinct "problem types" specific to their needs.它们旨在供 HTTP API 重用,这些 API 可以识别特定于其需求的不同“问题类型”。

This is a web standard to communicate errors between machines, so it is a choice which makes sense in the context of API controllers error handling.这是一个 web 标准,用于在机器之间传达错误,因此在 API 控制器错误处理的上下文中,这是一个有意义的选择。

Notice that this standard has been designed to avoid reinventing the wheel each time, by defining custom error contracts to communicate errors from web apis and having to document them for clients.请注意,该标准旨在通过定义自定义错误契约来传达来自 web api 的错误并必须为客户记录它们,从而避免每次都重新发明轮子。

This documentation explains that for some HTTP status codes indicating a client error (eg: your 415 Unsopported Media Type status code) a JSON response compliant with the Problem Details specification is automatically created by the ASP.NET core framework. 本文档说明,对于某些指示客户端错误的 HTTP 状态代码(例如:您的415 Unsupported Media Type状态代码),符合问题详细信息规范的 JSON 响应由 ASP.NET 核心框架自动创建。

You have a few choices here:您在这里有几个选择:

  1. you can decide to suppress this behavior by setting ApiBehaviorOptions.SuppressMapClientErrors = true;您可以通过设置ApiBehaviorOptions.SuppressMapClientErrors = true; . . This way you'll get a plain status code (the 415 status code in your case) as the response, with no response content at all.这样你会得到一个简单的状态代码(在你的情况下是 415 状态代码)作为响应,根本没有响应内容。
  2. you can decide to customize some properties of the returned Problem Details JSON payload, as documented here您可以决定自定义返回的 Problem Details JSON 负载的一些属性,如此所述
  3. you can decide to take control of the generation of the Problem Details JSON response, by providing your own implementation of ProblemDetailsFactory as documented here您可以决定控制问题详细信息 JSON 响应的生成,方法是提供您自己的ProblemDetailsFactory实现,如此所述

I would avoid option 3.我会避免选项 3。

In my opinion the best thing you can do is sticking with option 2, if you really want to customize the error message contained inside the problem details JSON object.在我看来,如果您真的想自定义包含在问题详细信息 JSON object 中的错误消息,那么您可以做的最好的事情就是坚持使用选项 2。

Here is an example:这是一个例子:

public void ConfigureServices(IServiceCollection services)
{
  services
    .AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      options.ClientErrorMapping[415].Title = "My custom message";
    });
}

The response content you will get (for an invalid request, a request with no content for example) is the following:您将得到的响应内容(对于无效请求,例如没有内容的请求)如下:

自定义问题详细信息标题

Another option you can evaluate is option 1: suppressing the default client error handling done by ASP.NET core and take full control of the response in case of a 415 response status code.您可以评估的另一个选项是选项 1:抑制由 ASP.NET 核心完成的默认客户端错误处理,并在 415 响应状态代码的情况下完全控制响应。

This is an example:这是一个例子:

public void ConfigureServices(IServiceCollection services)
{
  services
    .AddControllers()
    .ConfigureApiBehaviorOptions(options =>
    {
      options.SuppressMapClientErrors = true;
    });
}

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
  if (env.IsDevelopment())
  {
    app.UseDeveloperExceptionPage();
  }

  app.Use(async (context, next) =>
  {
    await next();

    // Handle the 415 response
    if (context.Response.StatusCode == 415)
    {
      context.Response.ContentType = "application/json";

      var json = JsonSerializer.Serialize(new { message = "You need to add a request body!" });
      await context.Response.WriteAsync(json);
    }
  });

  app.UseRouting();

  app.UseAuthorization();

  app.UseEndpoints(endpoints =>
  {
    endpoints.MapControllers();
  });
}

The response payaload you will get (for an invalid request, a request with no content for example) is the following:您将获得的响应负载(对于无效请求,例如没有内容的请求)如下:

自定义回复内容

Notice that here I'm able to take full control of the response content because, by disabling the default client error mapping done by ASP.NET core ( options.SuppressMapClientErrors = true; ), the response you get when sending an invalid POST request is a plain 415 status code, with no response content at all .请注意,在这里我能够完全控制响应内容,因为通过禁用由 ASP.NET 核心 ( options.SuppressMapClientErrors = true; ) 完成的默认客户端错误映射,发送无效 POST 请求时得到的响应是一个普通的415状态码,完全没有响应内容 So, when writing to the response stream ( await context.Response.WriteAsync(json); ), you are writing inside an empty response stream, so only our custom JSON is sent to the client as the response content .因此,当写入响应 stream ( await context.Response.WriteAsync(json); ) 时,您正在写入一个空响应 stream,因此只有我们自定义的 JSON 作为响应内容发送到客户端

In your first attempt you forgot to disable the automatic response created by ASP.NET core in case of 415 response status code, so the code await context.Response.WriteAsync("Unsupported media type handled");在您的第一次尝试中,您忘记禁用 ASP.NET 核心在 415 响应状态代码的情况下创建的自动响应,因此代码await context.Response.WriteAsync("Unsupported media type handled"); is appending content to the response stream, after the response content already written by the ASP.NET core framework automatically.正在将内容附加到响应 stream,响应内容已经由 ASP.NET 核心框架自动写入。

from here从这里

https://learn.microsoft.com/en-us/as.net/core/fundamentals/error-handling?view=as.netcore-6.0#exception-handler-lambda https://learn.microsoft.com/en-us/as.net/core/fundamentals/error-handling?view=as.netcore-6.0#exception-handler-lambda

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler(exceptionHandlerApp =>
    {
        exceptionHandlerApp.Run(async context =>
        {
            context.Response.StatusCode = StatusCodes.Status500InternalServerError;

            // using static System.Net.Mime.MediaTypeNames;
            context.Response.ContentType = Text.Plain;

            await context.Response.WriteAsync("An exception was thrown.");

            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();

            if (exceptionHandlerPathFeature?.Error is FileNotFoundException)
            {
                await context.Response.WriteAsync(" The file was not found.");
            }

            if (exceptionHandlerPathFeature?.Path == "/")
            {
                await context.Response.WriteAsync(" Page: Home.");
            }
        });
    });

    app.UseHsts();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM