有没有办法在Cro中多次使用request.body?

And*_*nus 9 cro raku

我正在编写一个使用 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一次有相同的行为。我这样做错了吗?

Jon*_*ton 9

如果想要在中间件中使用请求主体使其可用于标准请求处理程序,则中间件需要通过调用 来恢复它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来简化编写希望检查主体的中间件,但尚未实现。