0

我正在寻找一种方法来捕获从客户 Newtonsoft 的JsonConverter.

我创建了以下自定义转换器。JsonConverter类中的属性Config使用它。Config类用于发布配置对象并用于 Web API POST 方法(我使用的是 .NET Core 3.1)。

转换器工作正常,但是当抛出异常时,处理异常的中间件不会捕获它。例如,我预计中间件会在 HTTP 请求正文中为 nullMissingConfigTypeException时捕获type,但FuncinappBuilder.Run()永远不会被调用。从转换器抛出的任何异常都不会被中间件捕获。
由于中间件不处理异常,API 方法返回500没有 HTTP 响应体的 http 状态码。我想返回400我的自定义错误消息。

我的目标是(我需要同时实现):

  • 返回 http400错误而不是500and
  • 在 HTTP 响应正文中返回我的自定义错误(Error对象。请参阅下面的中间件)

我想知道是否有办法以某种方式捕获异常(使用中间件或其他方式)或修改 HTTP 响应正文(我必须能够识别发生了特定错误,以便我只能在错误时修改响应正文发生)

注意:我不想ModelState在我的控制器操作方法中使用(不想为每个方法添加某种错误检查代码)。

更新
中间件可以捕获控制器操作方法抛出的异常。

我的自定义转换器:

public class ConfigJsonConverter :  JsonConverter 
{
    public override object ReadJson(
        JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        ...

        var jObject = JObject.Load(reader);
        if (jObject == null) throw new InvalidConfigException();

        var type = jObject["type"] ?? jObject["Type"];
        if (type == null) throw new MissingConfigTypeException();

        var target = CreateConfig(jObject);
        serializer.Populate(jObject.CreateReader(), target);
        return target;
    }


    private static Config CreateConfig(JObject jObject)
    {
        var type = (string)jObject.GetValue("type", StringComparison.OrdinalIgnoreCase);
        if (Enum.TryParse<ConfigType>(type, true, out var configType))
        {
            switch (configType)
            {
                case ConfigType.TypeA:
                    return new ConfigA();
                case ConfigType.TypeB:
                    return new ConfigB();
            }
        }

        throw new UnsupportedConfigTypeException(type, jObject);
    }

配置类:

[JsonConverter(typeof(ConfigJsonConverter))]
public abstract class Config {...}

public class ConfigA : Config {...}

中间件:

// This is called in startup.cs
public static IApplicationBuilder UseCustomExceptionHandler(this IApplicationBuilder application)
{
    return application.UseExceptionHandler(appBuilder => appBuilder.Run(async context =>
    {
        var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
        var exception = exceptionHandlerPathFeature.Error;

        Error error;
        switch (exception)
        {
            case InvalidConfigException typedException:
                error = new Error
                {
                    Code = StatusCodes.Status400BadRequest,
                    Message = typedException.Message
                };
                break;

            case MissingConfigTypeException typedException:
                error = new Error
                {
                    Code = StatusCodes.Status400BadRequest,
                    Message = typedException.Message
                };
                break;
            .....
        }

        var result = JsonConvert.SerializeObject(error);
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = error.Code;

        await context.Response.WriteAsync(result);
    }));
}

更新:
Startup.cs

public virtual void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILogger<Startup> logger)
{
    if (EnableHttps)
        app.UseHsts();
    ...

    app
        .UseForwardedHeaders()
        .UsePathBase(appConfig.BasePath);

    if (EnableHttps)
        app.UseHttpsRedirection();
    app
        .UseRouting()
        .UseEndpoints(endpoints =>
        {
            endpoints.MapHealthChecks("/health");
            endpoints.MapControllers();
        })
        .UseCustomExceptionHandler(logger);
4

1 回答 1

2

UseCustomExceptionHandler在设置端点和路由之前尝试添加:

app
    .UseCustomExceptionHandler(logger)
    .UseRouting()
    .UseEndpoints(endpoints =>
    {
        endpoints.MapHealthChecks("/health");
        endpoints.MapControllers();
    });

同样基于文档的异常处理通常是在管道中设置的第一个,甚至在app.UseHsts().

于 2020-06-27T12:17:55.093 回答