Microsoft 的最佳实践 规定使用预定义的 .NET 异常类型,并引发异常而不是返回错误代码。我希望异常捕获器能够区分我手动抛出的异常和由依赖项或意外运行时错误引发的异常。
除了广泛的字符串解析之外,还有什么方法可以使用预定义的异常来做到这一点?
我一直在考虑创建通用异常,例如
public abstract class AppException : Exception;
public class AppException<T> : AppException where T : Exception { ... }
这样我就可以
void DoTheThing(int input)
{
// run some code, including invoking stuff from 3rd party libraries,
// but there may be problems that trigger their own ArgumentException
// that have nothing to do with the input.
// I could try/catch, but I do not want to log or process it here.
if (invalidInput)
{
throw new AppException<ArgumentException>();
}
// run more code; again, I don't want to have to try/catch
// unexpected ArgumentExceptions and log/process it.
}
try
{
DoTheThing(inputInt);
}
catch (AppException<InvalidOperationException> ex) { ... }
catch (AppException ex) { ... }
catch (Exception ex) { ... }
如果我在没有自定义类型的情况下执行此操作,而只是
throw new InvalidOperationException()
,那么我无法做出这种区分,但这违反了最佳实践。或者我对于使用内置异常的方向过于迂腐?或者有没有办法确定我不知道的区别?
我在决定自定义异常时使用的指导是:“我会以不同于其他任何异常的方式处理此自定义异常吗?”
例如。想象一下,我们构建一个 API 端点,用于接收 Post 中的一些 JSON,并尝试将 JSON 解析为我们期望的数据类型。
[Function(nameof(PostSomething))]
public async Task<HttpResponseData> PostSomething([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "my-api/data")] HttpRequestData req)
{
string posted = await new StreamReader(req.Body).ReadToEndAsync();
MyData data = JsonSerializer.Deserialize<MyData>(posted); // Might cause JsonException
}
(注意,有更简单的方法可以从 Azure Fn 端点反序列化 JSON - 这只是为了说明)
如果客户端向我们的端点发布了无效的 JSON,那么目前这会抛出一个
JsonException
(如果未处理)会在您的 API 中导致 500 错误。
但是,您确实想抛出某种 400 错误,告诉客户端他们发送了无效数据。
所以你可能会说:“好吧,我们会在 API 的某些中间件中捕获所有
JsonExceptions
,并将它们全部转换为 400 个响应”
这会导致一个问题。如果您的代码由于“不同”原因抛出“
JsonException
”怎么办?也许您正在阅读您的应用程序配置,但它已损坏。您最终会生成 400 错误,因为这不是客户端的错误。
这就是自定义异常类型的用武之地。更改上面的代码:
[Function(nameof(PostSomething))]
public async Task<HttpResponseData> PostSomething([HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "my-api/data")] HttpRequestData req)
{
string posted = await new StreamReader(req.Body).ReadToEndAsync();
MyData data;
try
{
data = JsonSerializer.Deserialize<MyData>(posted);
}
catch (JsonException ex)
{
// If there's a JsonException then we know it's the fault of the client's data.
throw new ClientSentBadDataException(jex);
}
}
然后在错误处理中间件中,或者在顶层处理错误的地方,您可以以不同于任何其他
JsonException
的方式满足该异常类型。
例如在 Azure Function 中间件处理程序中:public class ExceptionMiddleware : IFunctionsWorkerMiddleware
{
private readonly ILogger<ExceptionMiddleware> _logger;
public ExceptionMiddleware(ILogger<ExceptionMiddleware> logger)
{
_logger = logger;
}
public async Task Invoke(FunctionContext context, FunctionExecutionDelegate next)
{
try
{
await next(context);
}
catch (ClientSentBadDataException bdex)
{
var response = (await context.GetHttpRequestDataAsync())?.CreateResponse();
response.StatusCode = HttpStatusCode.BadRequest;
context.GetInvocationResult().Value = response;
}
catch (Exception ex) // including any other cases of JsonException
{
// whatever else, e.g. log and re-throw.
}
}