基于角色的RESTful资源的应用程序结构

时间:2010-09-26 03:36:14

标签: ruby-on-rails restful-authentication

在使用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

3 个答案:

答案 0 :(得分:6)

我建议使用最后两个提议的实现的组合。他们遵循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这样的宝石,为您设置所有这些。

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

答案 1 :(得分:0)

虽然他们都处理创建故障单,但代理/故障单销售与客户/故障单购买似乎与我不同,他们应该分开。最终它们可能会进一步分歧,因为它们的使用方式与开头的方式不同。

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

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.

答案 2 :(得分:-1)

如果您对客户和代理商票使用相同的模型,那么在控制器中处理它们的方式应该没有重大区别。因此,创建动作将始终如下:

@ticket = Ticket.new(params[:ticket])

if @ticket.save
  redirect_to @ticket
else
  render :action => "new"
end

但您的观点可以简单定制:

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