gpt4 book ai didi

.net - application/problem+json 和 .net 6 最小 API?

转载 作者:行者123 更新时间:2023-12-03 08:16:04 24 4
gpt4 key购买 nike

我创建了一个非常基本的项目,它只是抛出一个异常,期望得到一个 application/problem+json 响应,但我得到了一个包含错误详细信息的 html 页面。我猜这是我读过的一些帖子谈论的“开发人员异常页面”,但我没有将其添加到启动中。

完整代码示例:

using Microsoft.AspNetCore.Builder;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", () => {
throw new System.Exception("Hello, World!");
return new string[] { };
});

app.Run();

我在谷歌上找不到任何相关的内容,我认为这是因为它非常新鲜。

如果不在 Visual Studio 中运行,行为会改变吗?我可以更改它,以便在 Visual Studio 中运行时也获得 application/problem+json 吗?

最佳答案

在“开发”环境中运行时(即 IWebHostEnvironment.IsDevelopment() 为 true),开发人员异常页面现在默认打开(在 .NET 6 中)。

通常,在 ASP.NET Core 应用程序中配置异常处理时,在开发与生产(或更严格地说,非开发)期间将采用单独的路径进行异常处理。开发人员异常页面(顾名思义)仅用于开发,而异常处理程序中间件 (app.UseExceptionHandler) 适用于非开发场景。

要返回异常的问题详细信息响应,您需要分别配置两个路径。开发人员异常页面有一个通过 IDeveloperPageExceptionFilter 接口(interface)的插件模型,可用于控制异常的呈现方式。 Here's an example当客户端指示它支持 JSON 时,过滤器将异常呈现为问题详细信息:

using Microsoft.AspNetCore.Mvc;
using Microsoft.Net.Http.Headers;

namespace Microsoft.AspNetCore.Diagnostics;

/// <summary>
/// Formats <see cref="DeveloperExceptionPageMiddleware"/> exceptions as JSON Problem Details if the client indicates it accepts JSON.
/// </summary>
public class ProblemDetailsDeveloperPageExceptionFilter : IDeveloperPageExceptionFilter
{
private static readonly object ErrorContextItemsKey = new object();
private static readonly MediaTypeHeaderValue _jsonMediaType = new MediaTypeHeaderValue("application/json");

private static readonly RequestDelegate _respondWithProblemDetails = RequestDelegateFactory.Create((HttpContext context) =>
{
if (context.Items.TryGetValue(ErrorContextItemsKey, out var errorContextItem) && errorContextItem is ErrorContext errorContext)
{
return new ErrorProblemDetailsResult(errorContext.Exception);
}

return null;
}).RequestDelegate;

public async Task HandleExceptionAsync(ErrorContext errorContext, Func<ErrorContext, Task> next)
{
var headers = errorContext.HttpContext.Request.GetTypedHeaders();
var acceptHeader = headers.Accept;

if (acceptHeader?.Any(h => h.IsSubsetOf(_jsonMediaType)) == true)
{
errorContext.HttpContext.Items.Add(ErrorContextItemsKey, errorContext);
await _respondWithProblemDetails(errorContext.HttpContext);
}
else
{
await next(errorContext);
}
}
}

internal class ErrorProblemDetailsResult : IResult
{
private readonly Exception _ex;

public ErrorProblemDetailsResult(Exception ex)
{
_ex = ex;
}

public async Task ExecuteAsync(HttpContext httpContext)
{
var problemDetails = new ProblemDetails
{
Title = $"An unhandled exception occurred while processing the request",
Detail = $"{_ex.GetType().Name}: {_ex.Message}",
Status = _ex switch
{
BadHttpRequestException ex => ex.StatusCode,
_ => StatusCodes.Status500InternalServerError
}
};
problemDetails.Extensions.Add("exception", _ex.GetType().FullName);
problemDetails.Extensions.Add("stack", _ex.StackTrace);
problemDetails.Extensions.Add("headers", httpContext.Request.Headers.ToDictionary(kvp => kvp.Key, kvp => (string)kvp.Value));
problemDetails.Extensions.Add("routeValues", httpContext.GetRouteData().Values);
problemDetails.Extensions.Add("query", httpContext.Request.Query);
var endpoint = httpContext.GetEndpoint();
if (endpoint != null)
{
var routeEndpoint = endpoint as RouteEndpoint;
var httpMethods = endpoint?.Metadata.GetMetadata<IHttpMethodMetadata>()?.HttpMethods;
problemDetails.Extensions.Add("endpoint", new {
endpoint?.DisplayName,
routePattern = routeEndpoint?.RoutePattern.RawText,
routeOrder = routeEndpoint?.Order,
httpMethods = httpMethods != null ? string.Join(", ", httpMethods) : ""
});
}

var result = Results.Json(problemDetails, statusCode: problemDetails.Status, contentType: "application/problem+json");

await result.ExecuteAsync(httpContext);
}
}

public static class ProblemDetailsDeveloperPageExtensions
{
/// <summary>
/// Adds a <see cref="IDeveloperPageExceptionFilter"/> that formats all exceptions as JSON Problem Details to clients
/// that indicate they support JSON via the Accepts header.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/></param>
/// <returns>The <see cref="IServiceCollection"/></returns>
public static IServiceCollection AddProblemDetailsDeveloperPageExceptionFilter(this IServiceCollection services) =>
services.AddSingleton<IDeveloperPageExceptionFilter, ProblemDetailsDeveloperPageExceptionFilter>();
}

你像这样在 DI 中注册它:

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddProblemDetailsDeveloperPageExceptionFilter();

var app = builder.Build();

对于非开发场景,您可以注册自己的端点来处理异常并在那里实现所需的行为,或者您可以使用像this one这样的中间件。 .

要自己执行此操作,您需要注册异常处理程序中间件并将其指向错误端点,该端点被写入以返回问题详细信息,like this :

...
var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/error");
}

var problemJsonMediaType = new MediaTypeHeaderValue("application/problem+json");
app.MapGet("/error", (HttpContext context) =>
{
var error = context.Features.Get<IExceptionHandlerFeature>()?.Error;
var badRequestEx = error as BadHttpRequestException;
var statusCode = badRequestEx?.StatusCode ?? StatusCodes.Status500InternalServerError;

if (context.Request.GetTypedHeaders().Accept?.Any(h => problemJsonMediaType.IsSubsetOf(h)) == true)
{
// JSON Problem Details
return error switch
{
BadHttpRequestException ex => Results.Extensions.Problem(detail: ex.Message, statusCode: ex.StatusCode),
_ => Results.Extensions.Problem()
};
}

// Plain text
context.Response.StatusCode = statusCode;
return Results.Text(badRequestEx?.Message ?? "An unhandled exception occurred while processing the request.");
})
.ExcludeFromDescription();
...

请注意,此示例中的某些部分使用 custom IResult implementation目前,由于即将发布的 ASP.NET Core 6.0 rc.2 版本中正在修复一个问题

关于.net - application/problem+json 和 .net 6 最小 API?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69351956/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com