19

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

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

我使用的是 Rails 3respond_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 

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

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

Rails 希望我的 @products 对象知道如何创建位置 URL。

澄清:@products第三方 API 返回的对象是纯 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关于介绍的帖子解释说,正在发生的事情是意料之中的。

我的问题是:为什么respond_with除了数据(和 HTTP 状态?)之外还有其他东西,而且显然只是为了POST.

我并不是说这是错误的,只是想了解Rails 实施的基本原理。

4

3 回答 3

16

简介:Rails 的基本原理来自 HTTP 和 REST。

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

现在解释一下。Rails 行为的基本原理植根于接受 HTTP 和 REST 约定。

只是为了从您所读到的内容与我将要详细说明的内容联系起来,我想提一下Ryan Daigle 关于默认 RESTful Rendering 的文章中的相关部分:

如果请求 :html 格式:

[删除了一些文字]

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

([括号中的]文字是我添加的。)

如果请求了另一种格式(即 :xml 或 :json)

[删除了一些文字]

  • 如果是 POST 请求,请在资源上调用 :to_format 方法并将其与 :created 状态和新创建资源的 :location 一起发送回”

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

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

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

在任何一种情况下,请注意新创建资源的位置是必需的。这就是为什么@products在上面的示例中需要同时包含数据和位置。

作为背景,我在 201 Created 上的 W3C 页面上分享了一些内容:

10.2.2 201 创建

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

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

于 2012-06-21T04:46:23.893 回答
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
于 2014-11-30T23:55:47.987 回答
1

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

我想不出什么好的理由。更重要的是,除了数据结构之外,我看不到 API 可以返回任何东西的任何方式!(这个问题对我来说没有意义!)

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

我想我在这里错过了重点,这似乎太明显了。(我怀疑您在上面的代码中遇到了另一个问题。)

于 2012-06-19T20:39:44.780 回答