我正在编写一个使用 request.body 并进行一些验证的中间件,如下所示:
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);
}
}
Run Code Online (Sandbox Code Playgroud)
我尝试多次使用 request.body 并且连接挂起。“inside”永远不会被打印(来自上面的示例)。
这是一个可重现的示例:
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}'";
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
当向该服务器发出请求时,curl -v 'http://127.0.0.1:10000' --data-raw 'some-text'它会在打印以下行后挂起:
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)
Run Code Online (Sandbox Code Playgroud)
request.body-text确实返回了一个承诺,我不确定我是否理解之后发生的事情。我尝试使用这个,但消费request.body一次有相同的行为。我这样做错了吗?
如果想要在中间件中使用请求主体并使其可用于标准请求处理程序,则中间件需要通过调用 来恢复它set-body。可以在 Cro OpenAPI 请求验证器中间件中找到一个工作示例。
对于您的示例,更改将是:
request-body-text -> $body {
put "in before-matched: `{$body}'";
request.set-body($body);
}
Run Code Online (Sandbox Code Playgroud)
添加调用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'
Run Code Online (Sandbox Code Playgroud)
已经提出了类似的方案peek-body来简化编写希望检查主体的中间件,但尚未实现。