在分布式應(yīng)用中,有時會發(fā)生不好的事情。有一個好的方法來處理錯誤是很重要的。
狀態(tài)處理器
@Error 注釋支持定義異常類或 HTTP 狀態(tài)。用@Error 注釋的方法必須在用@Controller 注釋的類中定義。該注釋還支持全局和局部的概念,局部是默認值。
本地錯誤處理程序僅響應(yīng)由于路由與同一控制器中的另一個方法匹配而引發(fā)的異常。任何拋出的異常都可以調(diào)用全局錯誤處理程序。在解析要執(zhí)行的處理程序時,總是首先搜索本地錯誤處理程序。
為異常定義錯誤處理程序時,您可以將異常實例指定為方法的參數(shù)并省略注釋的異常屬性。
本地錯誤處理
例如,以下方法在聲明控制器的范圍內(nèi)處理來自 Jackson 的 JSON 解析異常:
本地異常處理程序
Java |
Groovy |
Kotlin |
@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)
}
|
@Error
HttpResponse<JsonError> jsonError(HttpRequest request, JsonParseException e) { // (1)
JsonError error = new JsonError("Invalid JSON: " + e.message) // (2)
.link(Link.SELF, Link.of(request.uri))
HttpResponse.<JsonError>status(HttpStatus.BAD_REQUEST, "Fix Your JSON")
.body(error) // (3)
}
|
@Error
fun jsonError(request: HttpRequest<*>, e: JsonParseException): HttpResponse<JsonError> { // (1)
val error = JsonError("Invalid JSON: ${e.message}") // (2)
.link(Link.SELF, Link.of(request.uri))
return HttpResponse.status<JsonError>(HttpStatus.BAD_REQUEST, "Fix Your JSON")
.body(error) // (3)
}
|
聲明了一個顯式處理 JsonParseException 的方法
返回 JsonError 的一個實例。
返回自定義響應(yīng)以處理錯誤
本地狀態(tài)處理器
Java |
Groovy |
Kotlin |
@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)
}
|
@Error(status = HttpStatus.NOT_FOUND)
HttpResponse<JsonError> notFound(HttpRequest request) { // (1)
JsonError error = new JsonError("Person Not Found") // (2)
.link(Link.SELF, Link.of(request.uri))
HttpResponse.<JsonError>notFound()
.body(error) // (3)
}
|
@Error(status = HttpStatus.NOT_FOUND)
fun notFound(request: HttpRequest<*>): HttpResponse<JsonError> { // (1)
val error = JsonError("Person Not Found") // (2)
.link(Link.SELF, Link.of(request.uri))
return HttpResponse.notFound<JsonError>()
.body(error) // (3)
}
|
Error 聲明要處理的 HttpStatus 錯誤代碼(在本例中為 404)
為所有 404 響應(yīng)返回一個 JsonError 實例
返回 NOT_FOUND 響應(yīng)
全局錯誤處理
全局錯誤處理程序
Java |
Groovy |
Kotlin |
@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)
}
|
@Error(global = true) // (1)
HttpResponse<JsonError> error(HttpRequest request, Throwable e) {
JsonError error = new JsonError("Bad Things Happened: " + e.message) // (2)
.link(Link.SELF, Link.of(request.uri))
HttpResponse.<JsonError>serverError()
.body(error) // (3)
}
|
@Error(global = true) // (1)
fun error(request: HttpRequest<*>, e: Throwable): HttpResponse<JsonError> {
val error = JsonError("Bad Things Happened: ${e.message}") // (2)
.link(Link.SELF, Link.of(request.uri))
return HttpResponse.serverError<JsonError>()
.body(error) // (3)
}
|
@Error 將方法聲明為全局錯誤處理程序
為所有錯誤返回一個 JsonError 實例
返回 INTERNAL_SERVER_ERROR 響應(yīng)
全局狀態(tài)處理器
Java |
Groovy |
Kotlin |
@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)
}
|
@Error(status = HttpStatus.NOT_FOUND)
HttpResponse<JsonError> notFound(HttpRequest request) { // (1)
JsonError error = new JsonError("Person Not Found") // (2)
.link(Link.SELF, Link.of(request.uri))
HttpResponse.<JsonError>notFound()
.body(error) // (3)
}
|
@Error(status = HttpStatus.NOT_FOUND)
fun notFound(request: HttpRequest<*>): HttpResponse<JsonError> { // (1)
val error = JsonError("Person Not Found") // (2)
.link(Link.SELF, Link.of(request.uri))
return HttpResponse.notFound<JsonError>()
.body(error) // (3)
}
|
@Error 聲明要處理的 HttpStatus 錯誤代碼(在本例中為 404)
為所有 404 響應(yīng)返回一個 JsonError 實例
返回 NOT_FOUND 響應(yīng)
關(guān)于 @Error 注解的一些注意事項。您不能聲明相同的全局 @Error 注釋。不能在同一個控制器中聲明相同的非全局@Error 注解。如果具有相同參數(shù)的 @Error 注解作為全局存在而另一個作為本地存在,則本地注解優(yōu)先。
異常處理器
或者,您可以實現(xiàn) ExceptionHandler,這是一個通用掛鉤,用于處理在執(zhí)行 HTTP 請求期間發(fā)生的異常。
捕獲異常的 @Error 注釋優(yōu)先于捕獲相同異常的 ExceptionHandler 的實現(xiàn)。
內(nèi)置異常處理程序
Micronaut 附帶了幾個內(nèi)置的處理程序:
自定義異常處理程序
想象一下,當一本書缺貨時,您的電子商務(wù)應(yīng)用會拋出 OutOfStockException:
Java |
Groovy |
Kotlin |
public class OutOfStockException extends RuntimeException {
}
|
class OutOfStockException extends RuntimeException {
}
|
class OutOfStockException : RuntimeException()
|
與 BookController 一起:
Java |
Groovy |
Kotlin |
@Controller("/books")
public class BookController {
@Produces(MediaType.TEXT_PLAIN)
@Get("/stock/{isbn}")
Integer stock(String isbn) {
throw new OutOfStockException();
}
}
|
@Controller("/books")
class BookController {
@Produces(MediaType.TEXT_PLAIN)
@Get("/stock/{isbn}")
Integer stock(String isbn) {
throw new OutOfStockException()
}
}
|
@Controller("/books")
class BookController {
@Produces(MediaType.TEXT_PLAIN)
@Get("/stock/{isbn}")
internal fun stock(isbn: String): Int? {
throw OutOfStockException()
}
}
|
如果您不處理異常,服務(wù)器將返回 500(內(nèi)部服務(wù)器錯誤)狀態(tài)代碼。
要在拋出 OutOfStockException 時以 400 Bad Request 作為響應(yīng),您可以注冊一個 ExceptionHandler:
Java |
Groovy |
Kotlin |
@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)
}
}
|
@Produces
@Singleton
@Requires(classes = [OutOfStockException, ExceptionHandler])
class OutOfStockExceptionHandler implements ExceptionHandler<OutOfStockException, HttpResponse> {
private final ErrorResponseProcessor<?> errorResponseProcessor
OutOfStockExceptionHandler(ErrorResponseProcessor<?> errorResponseProcessor) {
this.errorResponseProcessor = errorResponseProcessor
}
@Override
HttpResponse handle(HttpRequest request, OutOfStockException e) {
errorResponseProcessor.processResponse(ErrorContext.builder(request)
.cause(e)
.errorMessage("No stock available")
.build(), HttpResponse.badRequest()) // (1)
}
}
|
@Produces
@Singleton
@Requirements(
Requires(classes = [OutOfStockException::class, ExceptionHandler::class])
)
class OutOfStockExceptionHandler(private val errorResponseProcessor: ErrorResponseProcessor<Any>) :
ExceptionHandler<OutOfStockException, HttpResponse<*>> {
override fun handle(request: HttpRequest<*>, exception: OutOfStockException): HttpResponse<*> {
return errorResponseProcessor.processResponse(
ErrorContext.builder(request)
.cause(exception)
.errorMessage("No stock available")
.build(), HttpResponse.badRequest<Any>()) // (1)
}
}
|
默認的 ErrorResponseProcessor 用于創(chuàng)建響應(yīng)的主體
格式錯誤
Micronaut 通過類型為 ErrorResponseProcessor 的 bean 生成錯誤響應(yīng)主體。
默認響應(yīng)主體是 vnd.error,但是您可以創(chuàng)建自己的 ErrorResponseProcessor 類型的實現(xiàn)來控制響應(yīng)。
如果需要自定義響應(yīng)而不是與錯誤相關(guān)的項目,則需要覆蓋處理異常的異常處理程序。
更多建議: