跳到主要内容

6.17 错误处理

有时候,分布式应用程序会发生不好的事情。有一个好的方法来处理错误是很重要的。

6.17.1 状态处理器

@Error 注解支持定义异常类或 HTTP 状态。用@Error 注解的方法必须在用 @Controller 注解的类中用定义。注解还支持全局和局部的概念,局部是默认的。

本地错误处理程序只响应由于路由与同一控制器中的另一个方法匹配而引发的异常。全局错误处理程序可以作为任何抛出的异常的结果调用。在解析要执行的处理程序时,总是首先搜索本地错误处理程序。

提示

为异常定义错误处理程序时,可以将异常实例指定为方法的参数,并省略注解的异常属性。

6.17.2 本地错误处理

例如,以下方法为声明控制器的作用域处理来自 Jackson 的 JSON 解析异常:

本地异常处理器

@Error
public HttpResponse<JsonError> jsonError(HttpRequest request, JsonParseException e) { // (1)
JsonError error = new JsonError("Invalid JSON: " + e.getMessage()) // (2)
.link(Link.SELF, Link.of(request.getUri()));

return HttpResponse.<JsonError>status(HttpStatus.BAD_REQUEST, "Fix Your JSON")
.body(error); // (3)
}
  1. 声明了一个显式处理 JsonParseException 的方法
  2. 返回 JsonError 的一个实例。
  3. 返回自定义响应以处理错误

本地状态处理器

@Error(status = HttpStatus.NOT_FOUND)
public HttpResponse<JsonError> notFound(HttpRequest request) { // (1)
JsonError error = new JsonError("Person Not Found") // (2)
.link(Link.SELF, Link.of(request.getUri()));

return HttpResponse.<JsonError>notFound()
.body(error); // (3)
}
  1. Error 声明要处理的 HttpStatus 错误代码(在本例中为 404)
  2. 为所有 404 响应返回 JsonError 实例
  3. 返回 NOT_FOUND 响应

6.17.3 全局错误处理

全局错误处理器

@Error(global = true) // (1)
public HttpResponse<JsonError> error(HttpRequest request, Throwable e) {
JsonError error = new JsonError("Bad Things Happened: " + e.getMessage()) // (2)
.link(Link.SELF, Link.of(request.getUri()));

return HttpResponse.<JsonError>serverError()
.body(error); // (3)
}
  1. @Error 将该方法声明为全局错误处理程序
  2. 为所有错误返回 JsonError 实例
  3. 返回 INTERNAL_SERVER_ERROR 响应

全局状态处理器

@Error(status = HttpStatus.NOT_FOUND)
public HttpResponse<JsonError> notFound(HttpRequest request) { // (1)
JsonError error = new JsonError("Person Not Found") // (2)
.link(Link.SELF, Link.of(request.getUri()));

return HttpResponse.<JsonError>notFound()
.body(error); // (3)
}
  1. @Error 声明要处理的 HttpStatus 错误代码(在本例中为 404)
  2. 为所有 404 响应返回 JsonError 实例
  3. 返回 NOT_FOUND 响应
警告

关于 @Error 注解需要注意的几点。不能声明相同的全局 @Error 注解。不能在同一控制器中声明相同的非全局 @Error 注解。如果具有相同参数的 @Error 注解以全局形式存在,而另一个以本地形式存在,则本地注解优先。

6.17.4 ExceptionHandler

或者,你可以实现 ExceptionHandler,这是一个用于处理 HTTP 请求执行过程中发生的异常的通用钩子。

警告

捕获异常的 @Error 注解优先于捕获同一异常的 ExceptionHandler 的实现。

6.17.4.1 内置异常处理器

Micronaut 提供了几个内置的处理器。

异常处理器
javax.validation.ConstraintViolationExceptionConstraintExceptionHandler
ContentLengthExceededExceptionContentLengthExceededHandler
ConversionErrorExceptionConversionErrorHandler
DuplicateRouteExceptionDuplicateRouteHandler
HttpStatusExceptionHttpStatusHandler
com.fasterxml.jackson.core.JsonProcessingExceptionJsonExceptionHandler
java.net.URISyntaxExceptionURISyntaxHandler
UnsatisfiedArgumentExceptionUnsatisfiedArgumentHandler
UnsatisfiedRouteExceptionUnsatisfiedRouteHandler
org.grails.datastore.mapping.validation.ValidationExceptionValidationExceptionHandler

6.17.4.2 自定义异常处理器

想象一下,当一本书缺货时,你的电子商务应用程序抛出 OutOfStockException

public class OutOfStockException extends RuntimeException {
}

添加到 BookController

@Controller("/books")
public class BookController {

@Produces(MediaType.TEXT_PLAIN)
@Get("/stock/{isbn}")
Integer stock(String isbn) {
throw new OutOfStockException();
}
}

如果不处理异常,服务器将返回 500(Internal Server Error,内部服务器错误)状态代码。

要在抛出 OutOfStockException 时以 400 Bad Request 作为响应,可以注册 ExceptionHandler

@Produces
@Singleton
@Requires(classes = {OutOfStockException.class, ExceptionHandler.class})
public class OutOfStockExceptionHandler implements ExceptionHandler<OutOfStockException, HttpResponse> {

private final ErrorResponseProcessor<?> errorResponseProcessor;

public OutOfStockExceptionHandler(ErrorResponseProcessor<?> errorResponseProcessor) {
this.errorResponseProcessor = errorResponseProcessor;
}

@Override
public HttpResponse handle(HttpRequest request, OutOfStockException e) {
return errorResponseProcessor.processResponse(ErrorContext.builder(request)
.cause(e)
.errorMessage("No stock available")
.build(), HttpResponse.badRequest()); // (1)
}
}
  1. 默认的 ErrorResponseProcessor 用于创建响应的主体

6.17.5 错误格式化

Micronaut 通过 ErrorResponseProcessor 类型的 bean 生成错误响应体。

默认的响应主体是 vnd.error,但是你可以创建自己的 ErrorResponseProcessor 类型的实现来控制响应。

如果需要自定义响应而不是与错误相关的项,则需要重写正在处理异常的异常处理程序。

英文链接