英文:
Is there a way to consume request.body multiple times in Cro?
问题
I'm writing a middleware that consumes request.body and does some validation like so:
before-matched {
request-body -> (:$email, :$captcha-token, :$captcha-solution, *%_) {
# Validate the email.
unless Email::Valid.mx($email).so {
response.status = 400;
content 'application/json', %(message => 'Invalid Email');
}
# Validate captcha.
unless $captcha.validate($captcha-token, $captcha-solution) {
response.status = 401;
content 'application/json', %(message => 'Invalid Captcha');
}
}
}
post -> 'api', 'subscribe' {
put "outside";
request-body -> (:$name, :$email, *%_) {
put "inside";
dd $name, $email;
content 'application/json', %(message => $name);
}
}
I tried consuming request.body multiple times and the connection hangs. "inside" is never printed (from the example above).
Here is a reproducible example:
use Cro::HTTP::Server;
use Cro::HTTP::Router;
sub MAIN() {
my Cro::Service $http = Cro::HTTP::Server.new(
http => <1.1>,
host => "127.0.0.1",
port => 10000,
application => routes()
);
$http.start;
put "Listening at http://127.0.0.1:10000";
react {
whenever signal(SIGINT) {
say "Shutting down...";
$http.stop;
done;
}
}
}
sub routes() {
route {
before-matched {
request-body-text -> $body {
put "in before-matched: `{$body}'";
}
}
post -> {
put "in post route before request-body-text";
dd request.body-text;
request-body-text -> $body {
put "in post route: `{$body}'";
}
}
}
}
When making a request to this server with curl -v 'http://127.0.0.1:10000' --data-raw 'some-text'
it hangs after printing these lines:
andinus@cadmium /tmp> raku cro-question-mre.raku
Listening at http://127.0.0.1:10000
in before-matched: `some-text'
in post route before request-body-text
Promise.new(scheduler => ThreadPoolScheduler.new(uncaught_handler => Callable), status => PromiseStatus::Planned)
request.body-text
does return a promise, I'm not sure I understand what is happening after that. I tried using this but consuming request.body
once has the same behavior. Am I doing this wrong?
英文:
I'm writing a middleware that consumes request.body and does some validation like so:
before-matched {
request-body -> (:$email, :$captcha-token, :$captcha-solution, *%_) {
# Validate the email.
unless Email::Valid.mx($email).so {
response.status = 400;
content 'application/json', %(message => 'Invalid Email');
}
# Validate captcha.
unless $captcha.validate($captcha-token, $captcha-solution) {
response.status = 401;
content 'application/json', %(message => 'Invalid Captcha');
}
}
}
post -> 'api', 'subscribe' {
put "outside";
request-body -> (:$name, :$email, *%_) {
put "inside";
dd $name, $email;
content 'application/json', %(message => $name);
}
}
I tried consuming request.body multiple times and the connection hangs. "inside" is never printed (from the example above).
Here is a reproducible example:
use Cro::HTTP::Server;
use Cro::HTTP::Router;
sub MAIN() {
my Cro::Service $http = Cro::HTTP::Server.new(
http => <1.1>,
host => "127.0.0.1",
port => 10000,
application => routes()
);
$http.start;
put "Listening at http://127.0.0.1:10000";
react {
whenever signal(SIGINT) {
say "Shutting down...";
$http.stop;
done;
}
}
}
sub routes() {
route {
before-matched {
request-body-text -> $body {
put "in before-matched: `{$body}'";
}
}
post -> {
put "in post route before request-body-text";
dd request.body-text;
request-body-text -> $body {
put "in post route: `{$body}'";
}
}
}
}
When making a request to this server with curl -v 'http://127.0.0.1:10000' --data-raw 'some-text'
it hangs after printing these lines:
andinus@cadmium /tmp> raku cro-question-mre.raku
Listening at http://127.0.0.1:10000
in before-matched: `some-text'
in post route before request-body-text
Promise.new(scheduler => ThreadPoolScheduler.new(uncaught_handler => Callable), status => PromiseStatus::Planned)
request.body-text
does return a promise, I'm not sure I understand what is happening after that. I tried using this but consuming request.body
once has the same behavior. Am I doing this wrong?
答案1
得分: 9
如果希望在中间件中既消耗请求体又使其可用于标准请求处理程序,那么中间件需要通过调用 set-body
来重新设置它。可以在 Cro OpenAPI 请求验证中间件的 工作示例 中找到。
对于您的示例,更改如下:
request-body-text -> $body {
put "in before-matched: `{$body}'";
request.set-body($body);
}
添加 set-body
调用会产生所需的输出:
in before-matched: `this is raw data'
in post route before request-body-text
Promise.new(scheduler => ThreadPoolScheduler.new(uncaught_handler => Callable), status => PromiseStatus::Planned)
in post route: `this is raw data'
已经提出了类似 peek-body
的建议,以便更轻松地编写希望检查请求体的中间件,但尚未实现。
英文:
If wanting to both consume the request body in middleware and make it available for the standard request handler, then the middleware needs to reinstate it by calling set-body
. A working example can be found in the Cro OpenAPI request validator middleware.
For your example, the change would be:
request-body-text -> $body {
put "in before-matched: `{$body}'";
request.set-body($body);
}
The addition of the set-body
call results in the desired output:
in before-matched: `this is raw data'
in post route before request-body-text
Promise.new(scheduler => ThreadPoolScheduler.new(uncaught_handler => Callable), status => PromiseStatus::Planned)
in post route: `this is raw data'
A peek-body
and similar has been proposed to ease writing middleware that wishes to inspect the body, but hasn't been implemented yet.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论