【问题标题】:Rails Routes based on condition基于条件的 Rails 路线
【发布时间】:2012-06-27 15:43:45
【问题描述】:

我有三个角色:讲师、学生、管理员,每个角色都有带有“主页”视图的控制器。

所以这很好用,

get "instructor/home", :to => "instructor#home"
get "student/home", :to => "student#home"
get "admin/home", :to => "admin#home"

我想写一个如下所示的虚 url,它将根据user_id 的角色路由到正确的主页。

get "/:user_id/home", :to => "instructor#home" or "student#home" or "admin#home"

我该如何做到这一点?

【问题讨论】:

  • 拥有一个“包罗万象”的控制器可能更简单,它可以根据用户的类型将用户重定向到正确的控制器/操作对。

标签: ruby-on-rails ruby-on-rails-3 routes ruby-on-rails-3.2 rails-routing


【解决方案1】:

我提供了另一种方法,因为在 Rails 中搜索基于角色的路由时,这个 SO 问题出现在顶部附近。

我最近需要实现类似的东西,但想避免在控制器中包含大量条件 - 由于我的每个用户角色都需要加载和呈现完全不同的数据,这使情况更加复杂。我选择使用Routing Constraint 将决定逻辑移至路由层。

# app/constraints/role_route_constraint.rb

class RoleRouteConstraint
  def initialize(&block)
    @block = block || lambda { |user| true }
  end

  def matches?(request)
    user = current_user(request)
    user.present? && @block.call(user)
  end

  def current_user(request)
    User.find_by_id(request.session[:user_id])
  end
end

上述代码中最重要的部分是matches? 方法,它将确定路由是否匹配。该方法传递了request 对象,该对象包含有关正在发出的请求的各种信息。就我而言,我正在查找存储在会话 cookie 中的 :user_id 并使用它来查找发出请求的用户。

然后,您可以在定义路线时使用此约束。

# config/routes.rb
Rails.application.routes.draw do
  get 'home', to: 'administrators#home', constraints: RoleRouteConstraint.new { |user| user.admin? }
  get 'home', to: 'instructors#home', constraints: RoleRouteConstraint.new { |user| user.instructor? }
  get 'home', to: 'students#home', constraints: RoleRouteConstraint.new { |user| user.student? }  
end

有了上述内容,向/home 发出请求的管理员将被路由到AdministratorsControllerhome 操作,向/home 发出请求的教师将被路由到InstructorsControllerhome 动作,以及向 /home 发出请求的学生将被路由到 StudentsControllerhome 动作。

更多信息

如果您正在寻找更多信息,我最近在my blog 上写了关于这种方法的文章。

【讨论】:

  • 绝对是比公认的更好的答案。它帮助我解决了一个稍微不那么复杂的问题,但是无法通过控制器中的逻辑来完成。谢谢!
  • 谢谢!在 routes.rb 中有条件重定向要好得多。
  • 很好的答案,我阅读并学到了很多关于约束的知识!
  • 我知道这样的事情是可能的,我讨厌在拥有所需的所有数据时进行重定向。有了这个请求,我就有了用户 ID,所以我应该能够在不发出另一个请求的情况下做出决定。所以谢谢你,这救了我。
【解决方案2】:

您不能对路由执行此操作,因为路由系统没有做出此决定所需的信息。所有 Rails 都知道此时请求的参数是什么,并且无权访问数据库中的任何内容。

你需要的是一个控制器方法,它可以加载任何需要的数据,大概是用户记录,并使用redirect_to进行相应的重定向。

这是相当标准的做法。

更新:

要在单个控制器操作中执行所有这些操作,您需要根据角色拆分逻辑。一个例子是:

class HomeController < ApplicationController
  def home
    case
    when @user.student?
      student_home
    when @user.admin?
      admin_home
    when @user.instructor
      instructor_home
    else
      # Unknown user type? Render error or use a default.
    end
  end

protected
  def instructor_home
    # ...
    render(:template => 'instructor_home')
  end

  def student_home
    # ...
    render(:template => 'student_home')
  end

  def admin_home
    # ...
    render(:template => 'admin_home')
  end
end

【讨论】:

  • 我正在考虑实现一个名为“user_redirect”的控制器方法并使用重定向来实现它。我的目标是在他们登录后立即显示一个像example.com/johndoe/home 这样的虚荣网址。我能做到吗?
  • 您希望浏览器中的 URL 是这样的吗?如果是这样,您需要在内部做一些事情来隐藏它。这通常涉及可以渲染许多不同模板之一的控制器,或者带有条件部分或有条件加载的部分的标准模板。
  • 是的,我希望浏览器中的 URL 是这样的。你能扩展一下吗?谢谢!
  • 使用相同的 URL 意味着必须执行相同的控制器动作,这反过来意味着动作需要能够处理所有不同的用例。请记住,您可以在控制器中使用 render(:template =&gt; '...') 呈现不同的模板。请参阅修改后的答案。
  • 你不能这样做 =/= 我不知道怎么做。
猜你喜欢
  • 2015-02-03
  • 1970-01-01
  • 1970-01-01
  • 2022-08-20
  • 2019-04-08
  • 2016-05-04
  • 2020-01-12
  • 2019-05-12
  • 1970-01-01
相关资源
最近更新 更多