【问题标题】:App structure for roles-based RESTful resources基于角色的 RESTful 资源的应用程序结构
【发布时间】:2011-04-17 07:49:43
【问题描述】:

在使用 RESTful 资源路由时,是否有一致的最佳方法来实现用户角色?

假设我有以下资源:

User has_many Tickets
Event has_many Tickets
Ticket belongs_to Person, Event

然后进一步说我有两种类型的用户:客户和代理。两者都将登录系统,但根据其角色具有不同的资源访问权限和功能。例如:

客户可以访问:

  • 事件索引,显示
  • 票证索引(按用户范围)、显示、购买/创建、返回/删除
  • 个人创建、显示、更新

代理可以访问:

  • 事件索引、显示、创建、更新、删除
  • 门票索引、显示、销售/创建、更新、退款/删除
  • 人物索引、显示、创建、更新、删除

以下 4 种通用方法中哪一种更简洁、更灵活?

角色文件夹中的独立控制器和命名空间中的资源,例如:

namespace "agent" do
  resources :events, :tickets, :people
end
namespace "customer" do
  resources :events, :tickets, :people
end

按角色分隔控制器,例如:

AgentController
  def sell_ticket, etc

CustomerController
  def buy_ticket, etc

在需要时具有单独操作的共享控制器,例如:

TicketController
  before_filter :customer_access, :only => :buy
  before_filter :agent_access, :except => :buy

  def buy  #accessed by customer to create ticket

  def sell   #accessed by agent to create ticket

与条件语句共享操作,例如:

TicketController
  def create
    if @role == :customer
      #buy ticket
    elsif @role == :customer
      #sell ticket
    end
  end

【问题讨论】:

    标签: ruby-on-rails restful-authentication


    【解决方案1】:

    我建议结合使用最后两个建议的实现。他们坚持 RESTful 表示,他们将授权置于适当的级别(控制器),并且是可扩展的实现。

    REST 本质上是关于accessing nouns with verbs。因此,您希望代理和客户执行与工单、用户和事件(名词)相关的操作(动词)。为了准确地表示这些名词,您应该为每个名词设置一个控制器。然后,客户可以通过 URL http://example.com/events/22 识别他们正在寻找的资源。从这里您可以使用 Rails 的路由来表示各种资源的上下文,即http://example.com/events/22/tickets,方法如下:

    resource :events do
      resource :tickets
    end
    

    通过坚持 RESTful 架构,您将购买 end to end principle。表示对象的范式只需要对此负责。它不应该尝试进行身份验证。那不是它的工作。授权应该发生在控制器中。我强烈建议您研究像 CanCanDeclarative Authorization 这样的宝石,为您设置所有这些。

    最后,此模型可扩展。通过将授权与资源的表示分开,您只需在需要时使用它。这使您的应用程序轻巧、灵活且简单。

    【讨论】:

      【解决方案2】:

      虽然他们都处理创建票证,但代理/票证销售与客户/票证购买在我看来差异很大,应该将它们分开。最终它们可能会进一步分化,因为它们的使用方式从一开始就如此不同。

      可以通过模块或从公共父控制器继承来共享控制器功能:

      module TicketsControllersCommom
        # common helper methods
      end
      
      class TicketsController < ApplicationController
        include TicketsControllersCommom
        # actions
      end
      
      class AgentTicketsController < ApplicationController
        include TicketsControllersCommom
        # actions
      end
      

      我可能会将代理部分视为一种管理部分,而客户部分是默认部分:

      /events/xx/tickets # collection
      /events/xx/tickets/xx # member
      # etc.
      /events/xx/agent/tickets # collection
      /events/xx/agent/tickets/xx # member
      # etc.
      

      或者,如果你有很多管理类型的东西,比如代理也管理事件,你可以命名整个部分:

      /agent/events/xx/tickets
      /agent/events/xx/edit
      # etc.
      

      【讨论】:

        【解决方案3】:

        如果您对客户和代理工单使用相同的模型,那么它们在控制器中的处理方式应该没有太大区别。所以,创建动作总是这样:

        @ticket = Ticket.new(params[:ticket])
        
        if @ticket.save
          redirect_to @ticket
        else
          render :action => "new"
        end
        

        但是您可以简单地自定义视图:

        <% if customer? %>
          Customer area.
        <% else %>
          Agent area.
        <% end %>
        

        【讨论】:

        • 模型不知道已经认证的用户。这通常由控制器管理。此外,OP 正在询问处理安全性的最佳方法。这不能在视图中自定义。 API 连接呢?您如何在那里为不同的用户角色实施安全性?您不想重复代码,因此它需要位于中心位置,即:控制器。所以问题是,如何合理地管理控制器以添加对用户角色和权限的这种精细控制。
        猜你喜欢
        • 1970-01-01
        • 2015-10-18
        • 2014-10-14
        • 1970-01-01
        • 2011-10-02
        • 1970-01-01
        • 2015-01-30
        • 2023-04-03
        • 2013-08-28
        相关资源
        最近更新 更多