在Cro中是否有一种方法可以多次使用request.body?

huangapple go评论58阅读模式
英文:

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.

huangapple
  • 本文由 发表于 2023年5月7日 15:54:22
  • 转载请务必保留本文链接:https://go.coder-hub.com/76192770.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定