0

0

Vertx 中的错误处理程序和失败处理程序

心靈之曲

心靈之曲

发布时间:2024-11-23 08:53:33

|

357人浏览过

|

来源于dev.to

转载

vertx 中的错误处理程序和失败处理程序

vert.x 是一个用于在 jvm 上开发反应式应用程序的工具包。我之前写过一篇简短的介绍性文章,当时我将它用于商业项目。几周前,我不得不重新审视一个基于 vert.x 的业余爱好项目,我了解到我对 vert.x 如何处理故障和错误的知识存在一些差距。为了填补这些空白,我做了一些实验,编写了一些测试,然后写了这篇博文。

大多数基于 vert.x 的 web 应用程序的核心是路由器。路由器根据请求的路径将请求路由到零个或多个请求处理程序。如果一切顺利,处理给定请求的处理程序将发出响应。当出现问题时,vert.x 提供故障处理程序和错误处理程序来处理这种情况。

如何发出请求处理程序中出现问题的信号?

请求处理程序中的错误有两种类型:要么抛出异常(有意或无意),要么通过在路由上下文上调用fail方法来显式发出错误信号。如果您想通过调用此方法来表明出现问题,您有以下三种选择:

  • 您可以提供状态代码,
  • 您可以提供状态代码和异常,或者
  • 您可以提供例外。

抛出异常与以异常作为参数调用fail方法具有相同的效果。如果调用失败时没有提供状态码,则使用状态码500。如果调用失败时提供了异常,则该异常将可供所有失败和错误处理程序使用。

如果没有任何错误或失败处理程序,vert.x 将响应失败的请求,状态代码为 500,正文包含“内部服务器错误”。如果该响应不适合您的需求,您需要注册一个错误处理程序和/或一个或多个失败处理程序。

错误处理程序

您可以向路由器的每个状态代码注册一个错误处理程序。如果在处理请求时发生某些故障并且没有故障处理程序(更多信息见下文),则为与该故障对应的状态代码注册的错误处理程序将处理该请求:

@test
void errorhandlercanhandleexception(vertxtestcontext vertxtestcontext) {
    var handlerexecuted = vertxtestcontext.checkpoint();
    var errorhandlerexecuted = vertxtestcontext.checkpoint();

    router.route("/")
            .handler(rc -> {
                handlerexecuted.flag();
                throw new runtimeexception(request_handler_error_message);
            });
    router.errorhandler(500, rc -> {
        errorhandlerexecuted.flag();
        rc.response()
                .setstatuscode(500)
                .end(message_from_error_handler + ": " + rc.failure().getmessage());
    });

    var response = performgetrequest("/");

    assertthat(response.statuscode()).isequalto(500);
    assertthat(response.body()).startswith(message_from_error_handler);
    assertthat(response.body()).endswith(request_handler_error_message);
    vertxtestcontext.succeedingthencomplete();
}

如上所述,错误处理程序可以访问导致调用错误处理程序的异常。在此示例中,状态代码 500 的错误处理程序处理该错误,因为这是未提供其他状态代码时的默认状态代码。

vert.x 支持使用子路由器将单个(大型)路由器拆分为多个较小的路由器。虽然可以为每个子路由器注册错误处理程序,但它们将被简单地忽略:

@test
void errorhandlerforsubrouterisignored(vertx vertx, vertxtestcontext vertxtestcontext) {
    var handlerexecuted = vertxtestcontext.checkpoint();
    var rooterrorhandlerexecuted = vertxtestcontext.checkpoint();

    var subrouter = router.router(vertx);
    subrouter.errorhandler(500, rc ->
            vertxtestcontext.failnow("error handler for sub router should not be reached"));
    subrouter.route("/route")
            .handler(rc -> {
                handlerexecuted.flag();
                throw new runtimeexception(request_handler_error_message);
            });

    router.route("/sub/*")
            .subrouter(subrouter);

    router.errorhandler(500, rc -> {
        rooterrorhandlerexecuted.flag();
        rc.response()
                .setstatuscode(500)
                .end(message_from_error_handler + ": " + rc.failure().getmessage());
    });

    var response = performgetrequest("/sub/route");

    assertthat(response.statuscode()).isequalto(500);
    assertthat(response.body()).startswith(message_from_error_handler);
    assertthat(response.body()).endswith(request_handler_error_message);
    vertxtestcontext.succeedingthencomplete();
}

故障处理程序

在某些情况下,您可能需要对错误的处理方式进行更细粒度的控制。这就是故障处理程序的用武之地。每个路由可以注册一个或多个故障处理程序。它们将按照注册的顺序处理错误,直到处理程序成功处理错误或引发异常。

与错误处理程序一样,失败处理程序可以访问导致其调用的异常。他们还可以访问状态代码:

成新网络商城购物系统
成新网络商城购物系统

使用模板与程序分离的方式构建,依靠专门设计的数据库操作类实现数据库存取,具有专有错误处理模块,通过 Email 实时报告数据库错误,除具有满足购物需要的全部功能外,成新商城购物系统还对购物系统体系做了丰富的扩展,全新设计的搜索功能,自定义成新商城购物系统代码功能代码已经全面优化,杜绝SQL注入漏洞前台测试用户名:admin密码:admin888后台管理员名:admin密码:admin888

下载
@test
void failurehandlercanhandlefailwithstatuscodeandexception(vertxtestcontext vertxtestcontext) {
    var handlerexecuted = vertxtestcontext.checkpoint();
    var failurehandlerexecuted = vertxtestcontext.checkpoint();

    router.route("/")
            .handler(rc -> {
                handlerexecuted.flag();
                rc.fail(418, new runtimeexception(request_handler_error_message));
            })
            .failurehandler(rc -> {
                failurehandlerexecuted.flag();
                rc.response()
                        .setstatuscode(rc.statuscode())
                        .end(message_from_failure_handler + ": " + rc.failure().getmessage());
            });

    var response = performgetrequest("/");

    assertthat(response.statuscode()).isequalto(418);
    assertthat(response.body()).startswith(message_from_failure_handler);
    assertthat(response.body()).endswith(request_handler_error_message);
    vertxtestcontext.succeedingthencomplete();
}

一旦失败处理程序成功处理失败,就不会调用任何错误处理程序:

@test
void errorhandlerisignoredwhenfailurehandlerhandledfailure(vertxtestcontext vertxtestcontext) {
    var handlerexecuted = vertxtestcontext.checkpoint();
    var failurehandlerexecuted = vertxtestcontext.checkpoint();

    router.route("/")
            .handler(rc -> {
                handlerexecuted.flag();
                throw new runtimeexception(request_handler_error_message);
            })
            .failurehandler(rc -> {
                failurehandlerexecuted.flag();
                rc.response()
                        .setstatuscode(rc.statuscode())
                        .end(message_from_failure_handler + ": " + rc.failure().getmessage());
            });
    router.errorhandler(500, rc -> vertxtestcontext.failnow("error should not reach error handler"));

    var response = performgetrequest("/");

    assertthat(response.statuscode()).isequalto(500);
    assertthat(response.body()).startswith(message_from_failure_handler);
    assertthat(response.body()).endswith(request_handler_error_message);
    vertxtestcontext.succeedingthencomplete();
}

如果一个故障处理程序无法处理某个故障,它可以让它由下一个故障处理程序处理:

@test
void failurehandlercandefertonextfailurehandler(vertxtestcontext vertxtestcontext) {
    var handlerexecuted = vertxtestcontext.checkpoint();
    var firstfailurehandlerexecuted = vertxtestcontext.checkpoint();
    var secondfailurehandlerexecuted = vertxtestcontext.checkpoint();

    router.route("/")
            .handler(rc -> {
                handlerexecuted.flag();
                throw new runtimeexception(request_handler_error_message);
            })
            .failurehandler(rc -> {
                firstfailurehandlerexecuted.flag();
                rc.next();
            })
            .failurehandler(rc -> {
                secondfailurehandlerexecuted.flag();
                rc.response()
                        .setstatuscode(rc.statuscode())
                        .end(message_from_failure_handler + ": " + rc.failure().getmessage());
            });

    var response = performgetrequest("/");

    assertthat(response.statuscode()).isequalto(500);
    assertthat(response.body()).startswith(message_from_failure_handler);
    assertthat(response.body()).endswith(request_handler_error_message);
    vertxtestcontext.succeedingthencomplete();
}

如果处理失败导致异常,则原始失败的处理将由错误处理程序接管:

@test
void exceptioninfailurehandlerisignoredbyerrorhandler(vertxtestcontext vertxtestcontext) {
    var handlerexecuted = vertxtestcontext.checkpoint();
    var failurehandlerexecuted = vertxtestcontext.checkpoint();
    var errorhandlerexecuted = vertxtestcontext.checkpoint();

    router.route("/")
            .handler(rc -> {
                handlerexecuted.flag();
                throw new runtimeexception(request_handler_error_message);
            })
            .failurehandler(rc -> {
                failurehandlerexecuted.flag();
                throw new runtimeexception(failure_handler_error_message);
            });

    router.errorhandler(500, rc -> {
        errorhandlerexecuted.flag();
        rc.response()
                .setstatuscode(500)
                .end(message_from_error_handler + ": " + rc.failure().getmessage());
    });

    var response = performgetrequest("/");

    assertthat(response.statuscode()).isequalto(500);
    assertthat(response.body()).startswith(message_from_error_handler);
    assertthat(response.body()).endswith(request_handler_error_message);
    vertxtestcontext.succeedingthencomplete();
}

如果没有为状态代码 500 注册错误处理程序,则故障处理程序中抛出的异常将导致内部服务器错误。

我们在上面看到在子路由器上注册的错误处理程序被忽略。然而,为子路由器上的路由注册的故障处理程序按预期运行。为子路由器的其中一个路由注册的失败处理程序可以返回响应本身,也可以回退到另一个匹配路由的失败处理程序:

@Test
void failureHandlerForSubRouterCanFallBackToFailureHandlerForRoot(Vertx vertx, VertxTestContext vertxTestContext) {
    var handlerExecuted = vertxTestContext.checkpoint();
    var rootFailureHandlerExecuted = vertxTestContext.checkpoint();
    var subFailureHandlerExecuted = vertxTestContext.checkpoint();

    var subRouter = Router.router(vertx);
    subRouter.route("/route")
            .handler(rc -> {
                handlerExecuted.flag();
                throw new RuntimeException(REQUEST_HANDLER_ERROR_MESSAGE);
            })
            .failureHandler(rc -> {
                subFailureHandlerExecuted.flag();
                rc.next();
            });

    router.route("/sub/*")
            .subRouter(subRouter);

    router.route()
            .failureHandler(rc -> {
                rootFailureHandlerExecuted.flag();
                rc.response()
                        .setStatusCode(500)
                        .end(MESSAGE_FROM_FAILURE_HANDLER + ": " + rc.failure().getMessage());
            });

    var response = performGetRequest("/sub/route");

    assertThat(response.statusCode()).isEqualTo(500);
    assertThat(response.body()).startsWith(MESSAGE_FROM_FAILURE_HANDLER);
    assertThat(response.body()).endsWith(REQUEST_HANDLER_ERROR_MESSAGE);
    vertxTestContext.succeedingThenComplete();
}

结论

正如我们所见,错误处理程序非常简单。实际上,每个状态代码只能有一个错误处理程序,并且如果该错误尚未以其他方式处理,则该处理程序将处理给定状态代码的每个错误。

关于失败处理程序还有更多要说的。每个路由可以有多个错误处理程序,它将按照处理程序注册的顺序处理错误。如果路由重叠(与给定请求的路径匹配的多个路由),则按照注册路由的顺序调用每个路由的故障处理程序。每个故障处理程序都可以决定让下一个故障处理程序处理错误。

我希望这篇文章能为 vert.x 的官方文档提供有用的补充。如果您想自己尝试一下,请克隆并浏览 https://github.com/ljpengelen/vertx-error-and-failure-handlers 以获得一些灵感和一个不错的起点。

相关专题

更多
scripterror怎么解决
scripterror怎么解决

scripterror的解决办法有检查语法、文件路径、检查网络连接、浏览器兼容性、使用try-catch语句、使用开发者工具进行调试、更新浏览器和JavaScript库或寻求专业帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

187

2023.10.18

500error怎么解决
500error怎么解决

500error的解决办法有检查服务器日志、检查代码、检查服务器配置、更新软件版本、重新启动服务、调试代码和寻求帮助等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

271

2023.10.25

http与https有哪些区别
http与https有哪些区别

http与https的区别:1、协议安全性;2、连接方式;3、证书管理;4、连接状态;5、端口号;6、资源消耗;7、兼容性。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1968

2024.08.16

Java 桌面应用开发(JavaFX 实战)
Java 桌面应用开发(JavaFX 实战)

本专题系统讲解 Java 在桌面应用开发领域的实战应用,重点围绕 JavaFX 框架,涵盖界面布局、控件使用、事件处理、FXML、样式美化(CSS)、多线程与UI响应优化,以及桌面应用的打包与发布。通过完整示例项目,帮助学习者掌握 使用 Java 构建现代化、跨平台桌面应用程序的核心能力。

36

2026.01.14

php与html混编教程大全
php与html混编教程大全

本专题整合了php和html混编相关教程,阅读专题下面的文章了解更多详细内容。

18

2026.01.13

PHP 高性能
PHP 高性能

本专题整合了PHP高性能相关教程大全,阅读专题下面的文章了解更多详细内容。

34

2026.01.13

MySQL数据库报错常见问题及解决方法大全
MySQL数据库报错常见问题及解决方法大全

本专题整合了MySQL数据库报错常见问题及解决方法,阅读专题下面的文章了解更多详细内容。

19

2026.01.13

PHP 文件上传
PHP 文件上传

本专题整合了PHP实现文件上传相关教程,阅读专题下面的文章了解更多详细内容。

16

2026.01.13

PHP缓存策略教程大全
PHP缓存策略教程大全

本专题整合了PHP缓存相关教程,阅读专题下面的文章了解更多详细内容。

6

2026.01.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
Git 教程
Git 教程

共21课时 | 2.7万人学习

Git版本控制工具
Git版本控制工具

共8课时 | 1.5万人学习

Git中文开发手册
Git中文开发手册

共0课时 | 0人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号