【问题标题】:How do I add more than one over method to a mojolicious route?如何向 mojolicious 路线添加多个 over 方法?
【发布时间】:2012-08-09 15:48:59
【问题描述】:

我有以下代码:

$r->find('user')->via('post')->over(authenticated => 1);

鉴于该路线,我可以通过经过身份验证的检查到达用户路线 这是使用 Mojolicious::Plugin::Authentication 设置的。

我想在该路线上添加另一个“over”。

$r->find('user')->via('post')->over(authenticated => 1)->over(access => 1);

不过,这似乎覆盖了经过身份验证的“结束”。

我尝试使用以下名称分解路线:

 my $auth = $r->route('/')->over(authenticated => 1)
     ->name('Authenticated Route');

 $access = $auth->route('/user')->over(access => 1)->name('USER_ACCESS');

但这根本不起作用。两个 'over' 都没有被访问。

我的路由是 /user、/item 之类的东西,使用 MojoX::JSON::RPC::Service 设置。 所以,我没有像 /user/:id 这样的东西来设置子路由。(不确定是否重要) 所有路由都像 /user,带参数发送。

我的情况如下:

$r->add_condition(
    access => sub {
        # do some stuff
    },
);

也就是$r->route('/user')->over(access => 1)中的'access';

简而言之,使用时路由可以正常工作:

$r->find('user')->via('post')->over(authenticated => 1);

但我无法添加第二条路线。

那么,在设置这些具有多个条件的路线时,我缺少什么? 是否可以将多个条件添加到单个路由/route_name?

【问题讨论】:

  • 我在实施 RBAC 时注意到了同样的事情。我希望基于特权的访问授权表现得像一棵树,这意味着链接。没有成功。我想这就是为什么他们给了我们桥梁。 :)
  • 我的问题是我在“add_condition”路由修饰符中有路由,如我上面的代码所示。所以,我无法与他们沟通。我想我可以将条件移动到一个模块中,作为一个函数,并使用一个桥接器。实际上,我将它放在了 before_dispatch 钩子中。

标签: perl mojolicious


【解决方案1】:

您可以在over 中使用这两个条件,就像在这个测试中一样:

use Mojolicious::Lite;

# dummy conditions storing their name and argument in the stash
for my $name (qw(foo bar)) {
    app->routes->add_condition($name => sub {
        my ($route, $controller, $to, @args) = @_;
        $controller->stash($name => $args[0]);
    });
}

# simple foo and bar dump action
sub dump {
    my $self = shift;
    $self->render_text(join ' ' => map {$self->stash($_)} qw(foo bar));
}

# traditional route with multiple 'over'
app->routes->get('/frst')->over(foo => 'yo', bar => 'works')->to(cb => \&dump);

# lite route with multiple 'over'
get '/scnd' => (foo => 'hey', bar => 'cool') => \&dump;

# test the lite app above
use Test::More tests => 4;
use Test::Mojo;

my $t = Test::Mojo->new;

# test first route
$t->get_ok('/frst')->content_is('yo works');
$t->get_ok('/scnd')->content_is('hey cool');

__END__
1..4
ok 1 - get /frst
ok 2 - exact match for content
ok 3 - get /scnd
ok 4 - exact match for content

在 perl 5.12.1 上与 Mojolicious 3.38 一起工作正常 - @DavidO 是对的,也许桥接器可以更好地完成这项工作。 :)

【讨论】:

    【解决方案2】:

    就我而言,我使用了两个under 方法:

    $r =  $app->routes;
    $guest =  $r->under->to( 'auth#check_level' );
    $user  =  $r->under->to( 'auth#check_level', { required_level => 100 } );
    $admin =  $r->under->to( 'auth#check_level', { required_level => 200 } );
    
    $guest->get( '/' )->to( 'main#index' );
    $user->get( '/user' )->to( 'user#show' );
    $super_admin =  $admin->under->to( 'manage#check_level', { super_admin => 100 } );
    $super_admin->get( '/delete_everything' )->to( 'system#shutdown' );
    

    在此示例中,当任何路由匹配某些under 时将被调用

    '/' -> auth#check_level -> main_index
    '/user' -> auth#check_level { required_level => 100 } -> 'user#show'
    '/delete_everything' -> auth#check_level { required_level => 200 } -> 'manage#check_level', { super_admin => 100 } -> 'system#shutdown'
    

    正如您在controller 中的目标action 之前所见,将运行另一个名为:auth#check_levelmanage#check_level 的操作

    在每个额外的操作中,您只需将stash->{ required_level } 与您在授权用户时设置的session->{ required_level } 进行比较

    package YourApp::Controller::Manage;
    
    sub check_level {
        my $self =  shift;
    
        my $user_have =  $self->session->{ required_level };
        my $we_require =  $self->stash->{ required_level };
        # 'system#shutdown' will be called if user has required level
        return 1   if  $user_have >= $we_require; 
    
    
        $self->redirect_to( '/you_have_no_access_rights' );
        return 0; #This route will not match. 'system#shutdown' will not be called
    }
    

    PS当然,我可以使用cb 或只是CODEREF,它们与控制器操作“非常相似”:

    $r->under({ cb => \&YourApp::Controller::auth::check_level });
    $r->under( \&YourApp::Controller::auth::check_level ); # "same"
    

    但我更喜欢->to( 'controller#action' ) 语法。看起来好多了

    【讨论】:

      【解决方案3】:

      如果我们使用这种方法会怎样?

      # register condition
      $r->add_condition(
          chain => sub {
              my ($route, $controller, $captures, $checkers) = @_; 
      
              for my $checker (@$checkers) {
                  return 0 unless $checker->($route, $controller, $captures);
              }
      
              return 1;
          },
      );
      
      # ...
      # example of using
      $r->get('/')->over(chain => [\&checker1, \&checker2])->to(cb => \&foo)->name('bar');
      

      【讨论】:

        猜你喜欢
        • 2012-12-30
        • 1970-01-01
        • 1970-01-01
        • 2018-10-07
        • 2017-02-07
        • 2012-10-10
        • 2021-02-19
        • 2020-07-03
        • 2014-09-03
        相关资源
        最近更新 更多