捕获自定义 JsonConverter 抛出的异常
Catch exception thrown from Custom JsonConverter
我正在寻找一种方法来捕获客户 Newtonsoft 的 JsonConverter
.
抛出的异常
我创建了以下自定义转换器。 JsonConverter
Config
class 中的属性使用它。 Config
class 用于 post 配置对象并用于 Web API POST 方法(我正在使用 .NET Core 3.1)。
转换器工作正常,但是当抛出异常时,处理异常的中间件没有捕获它。例如,当 HTTP 请求正文中的 type
为 null 时,我预计中间件会捕获 MissingConfigTypeException
,但是 appBuilder.Run()
中的 Func
永远不会被调用。从转换器抛出的任何异常都不会被中间件捕获。
因为异常没有被中间件处理,所以 API 方法 return 的 http 状态代码 500
没有 HTTP 响应主体。我想 return 400
我的自定义错误消息。
我的目标是(我需要同时实现):
- Return http
400
错误而不是 500
和
- Return 我在 HTTP 响应正文中的自定义错误(
Error
对象。请参阅下面的中间件)
我想知道是否有办法以某种方式(使用中间件或其他方式)捕获异常或修改 HTTP 响应主体(我必须能够识别发生的特定错误,以便我可以修改响应body 仅当错误发生时)
注意:我不想在我的控制器操作方法中使用 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);
}
配置class:
[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);
在设置端点和路由之前尝试添加您的UseCustomExceptionHandler
:
app
.UseCustomExceptionHandler(logger)
.UseRouting()
.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapControllers();
});
同样基于 docs 异常处理通常是管道中的第一个设置之一,甚至在 app.UseHsts()
.
之前
我正在寻找一种方法来捕获客户 Newtonsoft 的 JsonConverter
.
我创建了以下自定义转换器。 JsonConverter
Config
class 中的属性使用它。 Config
class 用于 post 配置对象并用于 Web API POST 方法(我正在使用 .NET Core 3.1)。
转换器工作正常,但是当抛出异常时,处理异常的中间件没有捕获它。例如,当 HTTP 请求正文中的 type
为 null 时,我预计中间件会捕获 MissingConfigTypeException
,但是 appBuilder.Run()
中的 Func
永远不会被调用。从转换器抛出的任何异常都不会被中间件捕获。
因为异常没有被中间件处理,所以 API 方法 return 的 http 状态代码 500
没有 HTTP 响应主体。我想 return 400
我的自定义错误消息。
我的目标是(我需要同时实现):
- Return http
400
错误而不是500
和 - Return 我在 HTTP 响应正文中的自定义错误(
Error
对象。请参阅下面的中间件)
我想知道是否有办法以某种方式(使用中间件或其他方式)捕获异常或修改 HTTP 响应主体(我必须能够识别发生的特定错误,以便我可以修改响应body 仅当错误发生时)
注意:我不想在我的控制器操作方法中使用 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);
}
配置class:
[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);
在设置端点和路由之前尝试添加您的UseCustomExceptionHandler
:
app
.UseCustomExceptionHandler(logger)
.UseRouting()
.UseEndpoints(endpoints =>
{
endpoints.MapHealthChecks("/health");
endpoints.MapControllers();
});
同样基于 docs 异常处理通常是管道中的第一个设置之一,甚至在 app.UseHsts()
.