Rails respond_with - 为什么POST返回URL而不是数据?

时间:2012-06-19 20:25:35

标签: ruby-on-rails-3 json rest respond-to

这是一个问题“它为什么会这样运作”,而不是“我如何使这项工作”。

我的应用程序正在调用返回JSON的第三方REST API,并将结果作为自己的 JSON API的一部分返回。

我使用的是Rails 3 respond_torespond_with方法;在GET请求的情况下,这可以正常工作,只需通过JSON。

对于POST,它会做得更多,包括从返回的对象中创建一个URL以传递:location选项。但由于我的对象只是JSON(不是ActiveRecord),我收到错误。

例如......

# POST /api/products.json with params id=:id
def create
  query_string = "#{user_id}&id=#{params[:id]}"
  @products = third_party_api_wrapper.products(query_string, 'POST')
  respond_with @products
end 

我的第三方API的包装器发出一个POST请求,返回正常,然后Rails返回500错误,记录如下:

NoMethodError (undefined method `{"response":{"message":"product 4e1712d9ec0f257c510013f8 selected"}}_url' for #<MyController> 

Rails希望我的@products对象知道如何制作位置网址。

澄清:第三方API返回的@products对象是纯JSON - 一个字符串,您可以在上面的错误日志消息中看到该字符串。发生此错误是因为Rails似乎希望对象更多 - 在Rails内部API支持中,它是一个ActiveRecord对象。

如果我用旧式

替换新的respond_with和sytax
respond_to do |format|
  format.json { render :json => @products }  # note, no :location or :status options
end
然后一切正常。这就是我所做的,所以我没有“如何”的问题,而是有一个“为什么”的问题。

Ryan Daigle's post在介绍中解释了预期会发生什么。

我的问题是:为什么 respond_with除了数据(以及HTTP状态?)之外还有其他任何内容,而且显然仅适用于POST

我并不是说这是错的,只是试图理解 Rails实现的基本原理。

3 个答案:

答案 0 :(得分:16)

摘要:Rails从HTTP和REST获取其基本原理。

(感谢您更新的问题。现在我理解您的核心问题:“我并不是说这是错误的,只是想了解Rails实现的基本原理。”)

现在解释一下。 Rails行为的基本原理源于拥抱HTTP和REST约定。

为了从您阅读的内容到我即将详细阐述的内容,我想提及Ryan Daigle's article on Default RESTful Rendering中的相关部分:

  

如果要求:html格式:

     

[删除了一些文字]

     
      
  • [在PUT或POST后没有验证错误]重定向到资源位置(即user_url)
  •   

(括号内的文字由我添加。)

  

如果要求另一种格式,(即:xml或:json)

     

[删除了一些文字]

     
      
  • 如果是POST请求,请在资源上调用:to_format方法,然后将其发送回:created status和新创建的资源的位置“
  •   

让我用我的话来说明Rails认为是好的做法:

  • 对于人类内容(例如HTML),在POST或PUT之后,服务器应该告诉浏览器通过303重定向到新创建的资源。这是常见的做法 - 非常有用,因为用户希望看到他们编辑的更新。

  • 对于机器内容(例如JSON,XML),在PUT之后,服务器应该只呈现201。在这种情况下,客户端使用API​​的程序可能会决定停在那里。 (毕竟,客户端指定了请求并得到了201,所以所有人都很荣幸。)这就是使用201(成功)而不是303(重定向)的原因。如果客户端想要请求新创建的资源,它可以使用Location标头查找它 - 但不应强制重定向。

在任何一种情况下都要注意新创建的资源的位置是必需的。这就是上述示例中的@products需要包含数据和位置的原因。

对于背景,我与W3C Page on 201 Created

分享了一些内容
  

10.2.2 201创建

     

请求已完成,并导致创建新资源。新创建的资源可以由响应实体中返回的URI引用,其中由Location头字段给出的资源的最具体URI。响应应该包括一个实体,其中包含资源特征和位置的列表,用户或用户代理可以从中选择最合适的资源特征和位置。实体格式由Content-Type头字段中给出的媒体类型指定。原始服务器必须在返回201状态代码之前创建资源。如果无法立即执行操作,服务器应该响应202(已接受)响应。

我希望这有助于解释其基本原理。我(天真?)的理解是,这个基本原理在Web框架中被广泛接受。从历史上看,我怀疑Rails对于REST和资源导向架构的许多热心支持者来说是热烈的实现基础(新词警报!)。

答案 1 :(得分:2)

@ david-james很好地回答了'为什么'。这只是通过respond_with回答的简短“方式”:

class Api::V1::UsersController < ApplicationController

  respond_to :json

  def create
    @user = User.create(...)
    respond_with @user, location: url_for([:api, :v1, @user])
  end

end

答案 2 :(得分:1)

要回答这个问题:“为什么API应该返回除数据以外的任何内容(以及HTTP状态?)。我并不是说这是错误的,只是试图了解其基本原理。”

我认为没有好的理由。更重要的是,我看不出任何API可以返回数据结构之外的任何内容! (这个问题对我来说没有意义!)

根据定义,API调用必须返回数据结构。 (它可能像字符串一样简单。它可能是JSON。它可能是XML。)它可以使用内容协商来决定格式。它可能是也可能不是严格的模式,但至少客户端库必须能够解析它。在任何情况下,API文档都应该非常清楚并坚持下去。客户端库如何期望互操作?

我想我在这里忽略了这一点,这似乎太明显了。 (我怀疑你上面的代码中有另一个问题。)