Micronaut 錯誤處理

2023-03-06 15:45 更新

在分布式應(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)
}
  1. 聲明了一個顯式處理 JsonParseException 的方法

  2. 返回 JsonError 的一個實例。

  3. 返回自定義響應(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)
}
  1. Error 聲明要處理的 HttpStatus 錯誤代碼(在本例中為 404)

  2. 為所有 404 響應(yīng)返回一個 JsonError 實例

  3. 返回 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)
}
  1. @Error 將方法聲明為全局錯誤處理程序

  2. 為所有錯誤返回一個 JsonError 實例

  3. 返回 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)
}
  1. @Error 聲明要處理的 HttpStatus 錯誤代碼(在本例中為 404)

  2. 為所有 404 響應(yīng)返回一個 JsonError 實例

  3. 返回 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)置的處理程序:

Exception

Handler

javax.validation.ConstraintViolationException

ConstraintExceptionHandler

ContentLengthExceededException

ContentLengthExceededHandler

ConversionErrorException

ConversionErrorHandler

DuplicateRouteException

DuplicateRouteHandler

HttpStatusException

HttpStatusHandler

com.fasterxml.jackson.core.JsonProcessingException

JsonExceptionHandler

java.net.URISyntaxException

URISyntaxHandler

UnsatisfiedArgumentException

UnsatisfiedArgumentHandler

UnsatisfiedRouteException

UnsatisfiedRouteHandler

org.grails.datastore.mapping.validation.ValidationException

ValidationExceptionHandler

自定義異常處理程序

想象一下,當一本書缺貨時,您的電子商務(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)
    }
}
  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)的項目,則需要覆蓋處理異常的異常處理程序。


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號