如何修复 Vert.x 中的 'Request has already been read' 错误
How to fix 'Request has already been read' error in Vert.x
我正在设置 api 网关。我想在请求 BE 服务之前验证授权令牌。我得到 IllegalStateException:请求已被读取。请帮忙。
我把测试项目代码上传到GitHub。
https://github.com/EddyPan/test-demo
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));
});
异常:
java.lang.IllegalStateException: Request has already been read
at io.vertx.core.http.impl.HttpServerRequestImpl.checkEnded(HttpServerRequestImpl.java:599)
at io.vertx.core.http.impl.HttpServerRequestImpl.setExpectMultipart(HttpServerRequestImpl.java:431)
at io.vertx.ext.web.impl.HttpServerRequestWrapper.setExpectMultipart(HttpServerRequestWrapper.java:208)
at com.demo.HttpServerVerticle.lambda$start(HttpServerVerticle.java:62)
at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:232)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:121)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:134)
at com.demo.HttpServerVerticle.authHandler(HttpServerVerticle.java:132)
at com.demo.HttpServerVerticle.lambda$null[=13=](HttpServerVerticle.java:53)
at io.vertx.ext.web.client.impl.HttpContext.handleDispatchResponse(HttpContext.java:285)
at io.vertx.ext.web.client.impl.HttpContext.execute(HttpContext.java:272)
at io.vertx.ext.web.client.impl.HttpContext.next(HttpContext.java:250)
at io.vertx.ext.web.client.impl.predicate.PredicateInterceptor.handle(PredicateInterceptor.java:69)
at io.vertx.ext.web.client.impl.predicate.PredicateInterceptor.handle(PredicateInterceptor.java:32)
at io.vertx.ext.web.client.impl.HttpContext.next(HttpContext.java:247)
at io.vertx.ext.web.client.impl.HttpContext.fire(HttpContext.java:257)
at io.vertx.ext.web.client.impl.HttpContext.dispatchResponse(HttpContext.java:218)
at io.vertx.ext.web.client.impl.HttpContext.lambda$null(HttpContext.java:341)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
at io.vertx.core.impl.EventLoopContext.lambda$executeAsync[=13=](EventLoopContext.java:38)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:495)
at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:905)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
我解决了这个问题。在调用 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));
});
您可能会遇到此错误的两个原因:
A) 在 BodyHandler
之前异步 Handler
如 Vert.X documentation 中所述 BodyHandler
:
The usage of this handler requires that it is installed as soon as possible in the router since it needs to install handlers to consume the HTTP request body and this must be done before executing any async call.
修复 1:更改顺序:
router.post(endpoint)
.consumes(contentType)
.handler(bodyHandler) <<<<<<<<< first this
.handler(authHandler) <<<<<<<< then this async handler;
修复 2:pause/resume 请求交付:
参见Vert.X documentation:
If an async call is required before, the HttpServerRequest should be paused and then resumed so that the request events are not delivered until the body handler is ready to process them.
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();
...
}
}
我正在设置 api 网关。我想在请求 BE 服务之前验证授权令牌。我得到 IllegalStateException:请求已被读取。请帮忙。
我把测试项目代码上传到GitHub。 https://github.com/EddyPan/test-demo
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));
});
异常:
java.lang.IllegalStateException: Request has already been read
at io.vertx.core.http.impl.HttpServerRequestImpl.checkEnded(HttpServerRequestImpl.java:599)
at io.vertx.core.http.impl.HttpServerRequestImpl.setExpectMultipart(HttpServerRequestImpl.java:431)
at io.vertx.ext.web.impl.HttpServerRequestWrapper.setExpectMultipart(HttpServerRequestWrapper.java:208)
at com.demo.HttpServerVerticle.lambda$start(HttpServerVerticle.java:62)
at io.vertx.ext.web.impl.RouteImpl.handleContext(RouteImpl.java:232)
at io.vertx.ext.web.impl.RoutingContextImplBase.iterateNext(RoutingContextImplBase.java:121)
at io.vertx.ext.web.impl.RoutingContextImpl.next(RoutingContextImpl.java:134)
at com.demo.HttpServerVerticle.authHandler(HttpServerVerticle.java:132)
at com.demo.HttpServerVerticle.lambda$null[=13=](HttpServerVerticle.java:53)
at io.vertx.ext.web.client.impl.HttpContext.handleDispatchResponse(HttpContext.java:285)
at io.vertx.ext.web.client.impl.HttpContext.execute(HttpContext.java:272)
at io.vertx.ext.web.client.impl.HttpContext.next(HttpContext.java:250)
at io.vertx.ext.web.client.impl.predicate.PredicateInterceptor.handle(PredicateInterceptor.java:69)
at io.vertx.ext.web.client.impl.predicate.PredicateInterceptor.handle(PredicateInterceptor.java:32)
at io.vertx.ext.web.client.impl.HttpContext.next(HttpContext.java:247)
at io.vertx.ext.web.client.impl.HttpContext.fire(HttpContext.java:257)
at io.vertx.ext.web.client.impl.HttpContext.dispatchResponse(HttpContext.java:218)
at io.vertx.ext.web.client.impl.HttpContext.lambda$null(HttpContext.java:341)
at io.vertx.core.impl.ContextImpl.executeTask(ContextImpl.java:320)
at io.vertx.core.impl.EventLoopContext.lambda$executeAsync[=13=](EventLoopContext.java:38)
at io.netty.util.concurrent.AbstractEventExecutor.safeExecute(AbstractEventExecutor.java:163)
at io.netty.util.concurrent.SingleThreadEventExecutor.runAllTasks(SingleThreadEventExecutor.java:404)
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:495)
at io.netty.util.concurrent.SingleThreadEventExecutor.run(SingleThreadEventExecutor.java:905)
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
at java.lang.Thread.run(Thread.java:748)
我解决了这个问题。在调用 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));
});
您可能会遇到此错误的两个原因:
A) 在 BodyHandler
之前异步 Handler
如 Vert.X documentation 中所述 BodyHandler
:
The usage of this handler requires that it is installed as soon as possible in the router since it needs to install handlers to consume the HTTP request body and this must be done before executing any async call.
修复 1:更改顺序:
router.post(endpoint)
.consumes(contentType)
.handler(bodyHandler) <<<<<<<<< first this
.handler(authHandler) <<<<<<<< then this async handler;
修复 2:pause/resume 请求交付:
参见Vert.X documentation:
If an async call is required before, the HttpServerRequest should be paused and then resumed so that the request events are not delivered until the body handler is ready to process them.
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();
...
}
}