croservices/cro-core

Route delegate transfrom flow

Closed this issue · 4 comments

Are there any examples of using a transform as part of delegate in a route? I am trying to figure out how to do some tests (csrf, sessions, user permissions, etc) in a delegate transform. Then on success pass the request to the intended route or delegate to further chained transforms. On failure respond with failure, bad-request, etc.

jnthn commented

I'm currently in the process of implementing these various supporting middleware bits, which will make such things easier to do. There's are also some convenience forms using before blocks planned.

It's possible to do such things today. One way is by writing two Cro::Transforms and using Cro::ConnectionState to establish a link between them. The first one, when it wants to send an early response, emits the response into a Supply held in the connection state. The second one passes on responses both from that connection state and also from the normal response stream. This is the mechanism that the (far more convenient) Cro::HTTP::Middleware::Conditional role uses on the inside. You can see how it's done in this commit.

An alternative is to do a kind of conditional delegation, which would be something like (untested, just a sketch):

class CsrfDelegate does Cro::Transform {
    has Cro::Transform $.target;

    method consumes() { Cro::HTTP::Request }
    method produces() { Cro::HTTP::Response }

    method transformer($pipeline) {
        supply whenever $pipeline -> $request {
            if some-csrf-problem($request) {
                emit Cro::HTTP::Response.new(:$request, :403status);
            }
            else {
                whenever $!target.transformer(supply emit $request) -> $response {
                    emit $response;
                }
            }
        }
    }
}

Hi @jnthn,

Thanks. I was able to get the conditional delegation example working. This will make it a lot easier to keep all of these validations out of the routes.

Just a note for anyone else trying to do something similar, I had to use the <path prefix *> syntax to get the remainder of the path to pass through to the routes. The behavior is slightly different from route include where you don't need to add the *. It is mentioned in the docs.

jnthn commented

The latest release included the Cro::HTTP::Middleware bits that make this kind of thing easier (plus there's the block forms for middleware in Cro::HTTP::Router), so will close this issue now.

Thanks for the updates. I will mess around with this sometime this week.

ta