在RESTful设计中,支持不同类型GET的最佳方法是什么?

时间:2009-01-31 23:14:43

标签: ruby-on-rails rest

在当前项目中,我需要支持通过登录凭据和电子邮件地址查找用户。我知道在RESTful设计中,您使用GET来查找资源。在Rails ......

GET /users    # => UsersController.index -- find all the users

GET /users/1  # => UsersController.show -- find a particular user

但我也需要类似的东西......

GET /users?username=joe&password=mysterio

GET /users?email=foo@bar.com

indexshow之后添加其他路线和操作是否常规?

或者更常见的是在show动作中放置条件逻辑来查看参数并检测我们是否通过这样或那样的方式找到它?

PUT请求存在类似问题。在一种情况下,我需要将用户设置为“活动”(user.active = true),在另一种情况下,我只需要进行基于表单的一般编辑操作。

谢谢你们。最终我要弄清楚这些REST的东西。

5 个答案:

答案 0 :(得分:3)

我是SO的新手,因此我无法发表评论,但选中的绿色答案并非RESTful。

在RESTful世界中,您的控制器会抓取所有参数并将其传递给模型层进行处理。通常,您不应该创建其他操作。

相反,你应该做这样的事情:

def show
  @user = User.find_by_login_or_email(params[:user])
  ... #rest of your action
end

您的模型可以采用以下方法:

class User
  self.find_by_login_or_email(params)
    return find_by_login(params[:login]) unless params[:login].blank?
    return find_by_email(params[:email]) unless params[:email].blank?
    nil #both were blank
  end
end

您的观点可能如下所示:

<%= f.text_field :user, :email %>

<%= f.text_field :user, :login %>

注意:未经测试的代码,因此可能有错误......但总体思路通常不是为每个一次性规则创建新的操作。相反,看看你是否可以将逻辑推入模型中。如果您的控制器开始有太多非标准操作,那么可能是时候重新评估您的域建模了,也许它会将操作重构为一些新模型。

ps:你永远不应该通过像这样的GET传递密码

答案 1 :(得分:2)

您可以做的第一件事就是让您的GET尽可能聪明。在您的示例中,可以通过编程方式处理。参数可以这样处理:

  • 是号码吗?这是用户ID;
  • 有一个@吗?这是一封电子邮件;
  • 否则?这是一个用户名。

但我认为你不只是在谈论这个例子,而是想要处理一般情况而不仅仅是这个具体情况。

基本上有两种方法可以解决这个问题:

  1. 添加额外的路径信息,例如/users/email/me@here.com,/ users / name / cletus;或
  2. 在您的“顶级”网址中更具体,例如/ user-by-email/me@here.com,/ user-by-name / cletus。
  3. 如果可以,我会以编程方式处理它。

答案 2 :(得分:2)

我不知道这是多少惯例,但这就是我要做的。一世 会添加另一个动作,只要它与此具体相关 资源。在您的示例中,show是userid的find,因此它是有意义的 UsersController上的另一个操作。你可以把它变成一个句子 感觉,“用这个电子邮件地址给我这个用​​户”

对于另一个,GET /users?username=joe&password=mysterio,我愿意 那是另一种资源。我假设你认为行动会登录 用户密码是否正确。动词GET没有意义 上下文。

你可能想要一个'会话'资源(BTW,这就是restful_auth的工作原理)。 所以你会说“为这个用户创建一个会话”,或类似POST /sessions的帖子,其中帖子的主体是用户名&amp;用户的密码。 这也具有不在历史中保存密码的良好副作用 或者让某人在HTTP代理上捕获它。

所以你的控制器代码看起来像这样:

class UsersController < ActionController::Base

    def show
      @user = User.find_by_id(params[:id])
      # etc ...
    end

    def show_by_email
      @user = User.find_by_email(params[:email)
    end
end

class SessionsController < ActionController::Base
  def create
     # ... validate user credentials, set a cookie or somehow track that the 
     # user is logged in to be able to authenticate in other controllers
  end
end

您可以像这样设置路线:

map.connect "/users/byemail", :controller => "users", :action => "show_by_email", :conditions => { :method => :get }
map.resources :users
map.resources :sessions

这会为您提供/users/byemail?email=foo@example.com之类的网址。有 直接在URL路径中编码电子邮件的问题,rails看到'.com' 最后,默认情况下将其转换为:format。可能有一个 绕过它,但这就是我的工作。

也像cletus所说,有一些方法可以根据URL的各个部分的格式来匹配你的路线,比如所有的数字或字母数字,但是我不知道如何使用网址。

答案 3 :(得分:2)

关于“ByEmail”请求,您是否考虑过创建新的电子邮件资源。

GET /email/foo_at_bar_dot_com

响应可以包含指向相关用户的链接。

我看到很多人试图将RESTful设计原则应用于他们的URL结构,然后将这些URL映射到过程处理程序代码。例如GET =显示,或者是GET = Index或ShowByEmail。通过这样做,您实际上只是假装执行RESTful设计,然后尝试在面向资源的URL空间和面向过程的实现之间创建映射。这真的很难做到,程序性质不断泄漏到URL中。

面向资源的设计通常需要一种非常不同的方式来思考我们习惯的问题,不幸的是,许多框架一直在吸引我们回到RPC模型中。

答案 4 :(得分:1)

您可以为不同的任务设置不同的路线。因此,对于这种情况,您可以通过一个路由指向UserControll中的方法,以便通过电子邮件获取用户,另一个路径通过凭据获取信息。