如何修复Vert.x中的“请求已被读取”错误

时间:2019-08-31 16:36:59

标签: java vert.x

我正在设置一个api网关。我想在请求BE服务之前验证授权令牌。我收到了IllegalStateException:请求已被读取。请帮忙。

我将测试项目代码上传到GitHub。 https://github.com/EddyPan/test-demo

(apply #'funcall #'mapcar #'list '((a b) (c d) (e f)))

例外:

router.route().path("/user/admin").method(HttpMethod.POST)
        .handler(rct -> {

            HttpServerRequest request = rct.request().setExpectMultipart(true);

            MultiMap headers = request.headers();

            JsonObject param = new JsonObject().put("requestUrl", "http://localhost:18080/authorize")
                    .put("httpMethod", "POST");

            webClient.postAbs("http://localhost:18080/authorize")
                    .timeout(6000)
                    .putHeader("Content-Type", "application/json")
                    .putHeader("Authorization", headers.get("Authorization"))
                    .as(BodyCodec.jsonObject())
                    .sendJsonObject(param, ar -> authHandler(rct, ar));

        });

2 个答案:

答案 0 :(得分:0)

我解决了这个问题。在调用auth api之前,我先暂停原始请求,并在授权完成后恢复缓冲区处理。

router.route().path("/user/admin").method(HttpMethod.POST)
        .handler(rct -> {

            HttpServerRequest request = rct.request().setExpectMultipart(true);

            request.pause(); // Here is to pasue the origin request.

            MultiMap headers = request.headers();

            JsonObject param = new JsonObject().put("requestUrl", "http://localhost:18080/authorize")
                    .put("httpMethod", "POST");

            webClient.postAbs("http://localhost:18080/authorize")
                    .timeout(6000)
                    .putHeader("Content-Type", "application/json")
                    .putHeader("Authorization", headers.get("Authorization"))
                    .as(BodyCodec.jsonObject())
                    .sendJsonObject(param, ar -> authHandler(rct, ar));

        });

答案 1 :(得分:0)

导致此错误的两个原因:

A) 在 Handler 之前异步 BodyHandler

BodyHandler 的 Vert.X documentation 所述:

<块引用>

使用此处理程序要求它尽快安装在路由器中,因为它需要安装处理程序来使用 HTTP 请求正文,并且必须在执行任何请求之前完成此操作异步调用

修复 1:更改顺序:

router.post(endpoint)
   .consumes(contentType)
   .handler(bodyHandler)  <<<<<<<<< first this
   .handler(authHandler) <<<<<<<<  then this async handler;

修复 2:暂停/恢复请求传递:

参见 Vert.X documentation

<块引用>

如果之前需要异步调用,则应暂停然后恢复 HttpServerRequest,以便在主体处理程序准备好处理它们之前不会传递请求事件.

router.post(endpoint)
   .consumes(contentType)
   .handler(authHandler)
   .handler(bodyHandler);
BodyHandler implements Handler<RoutingContext> {
  @Override
  public void handle(final RoutingContext ctx) {

     // pause request delivery
     ctx.request().pause();

     asyncCall(r -> {
        // e.g. check authorization or database here

        // resume request delivery
        ctx.request.resume();

        // call the next handler
        ctx.next();
     }
  }
}

B) 多个 request.body() 调用

假设您使用 Vert.X BodyHandler 并在其后安装了自定义处理程序:

router.post(endpoint)
   .consumes(contentType)
   .handler(BodyHandler.create())
   .handler(customHandler);

您的自定义处理程序不得调用 request.body()! 否则您会得到

403: body has already been read

修正:使用ctx.getBody()

使用 ctx.getBody[/asJson/asString]() 获取已被 BodyHandler 读取的正文:

CustomHandler implements Handler<RoutingContext> {
    @Override
    public void handleAuthorizedRequest(RoutingContext ctx) {
        final var body = ctx.getBodyAsJson();

        // instead of: ctx.request().body();
        ...
    }
}