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?
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:
The addition of the
set-bodycall results in the desired output:A
peek-bodyand similar has been proposed to ease writing middleware that wishes to inspect the body, but hasn't been implemented yet.