.NET Core Web API:返回包含内容类型 application/problem+json 的自定义 HTT



ControllerBase包含诸如Conflict()之类的方法,这些方法返回从StatusCodeResult派生的ConflictResult对象(表示HTTP 409响应(。 生成的响应正文具有内容类型 application/problem+json,如下所示:

{
    "type": "https://tools.ietf.org/html/rfc7231#section-6.5.8",
    "title": "Conflict",
    "status": 409,
    "traceId": "0HLO99QHFC9QI:00000001"
}

HTTP 410 响应没有内置的方法/类,所以我做了一个:

[DefaultStatusCode(410)]
public class GoneResult : StatusCodeResult
{
    public GoneResult() : base(410)
    {}
}
...
public static class ControllerBaseExtensions
{
    public static GoneResult Gone(this ControllerBase controllerBase) // this doesn't give all the problem+JSON attributes
    {
        return new GoneResult();
    }
}

但是,这给了

{
    "type": "about:blank",
    "status": 410
}

即,type值不同,缺少titletraceId字段。

我还想为 HTTP 500 响应创建一个自定义类,其中包含一个带有错误消息的message字段。 我尝试返回 StatusCode(StatusCodes.Status500InternalServerError) ,这给了我与Gone()方法相同的最小application/problem+json响应;我也尝试返回StatusCode(StatusCodes.Status500InternalServerError, message),这给了我错误消息,但将响应格式化为 text/plain

生成ProblemDetails响应的代码不知道410状态代码,因此在生成响应对象时,它没有关联的LinkTitle属性。要添加此感知,请在 ConfigureServices 中配置ApiBehaviorOptions,如下所示:

services.Configure<ApiBehaviorOptions>(options =>
{
    options.ClientErrorMapping[410] = new ClientErrorData
    {
        Title = "Gone",
        Link = "https://tools.ietf.org/html/rfc7231#section-6.5.9"
    };
});

ClientErrorMappingint(状态代码(到 ClientErrorData 的字典。请注意,我用于上述Link的值确实指向 RFC 的正确部分。

简单地说,你必须实际返回一个ProblemDetails响应体。我必须深入研究代码才能确定,但我认为 ASP.NET Core 通过中间件执行此操作只是为了特定结果。他们的是 4xx 范围内的任何内容,但我认为这实际上仅限于返回该范围内状态代码的内置结果类型,而不是任何带有 4xx 状态代码的结果。同样,这是猜想,因为我还没有确切地看到他们在做什么,尽管它不是作为实际结果类的一部分发生的。

出于您的目的,有几种不同的方法可以处理此问题。您可以编写自己的中间件来捕获出站响应并重写它们。可以使用自定义异常处理程序。您可以简单地从ObjectResult继承,然后自己创建一个ProblemDetails实例并将其放入基实例中。您甚至可以直接从操作中返回ProblemDetails(尽管这显然是最不理想的方法(。

最新更新