Rails:respond_to 块是如何工作的?
Posted
技术标签:
【中文标题】Rails:respond_to 块是如何工作的?【英文标题】:Rails: How does the respond_to block work? 【发布时间】:2012-03-18 12:57:34 【问题描述】:我正在阅读Getting Started with Rails 指南,但对第 6.7 节感到困惑。生成脚手架后,我在控制器中找到以下自动生成的块:
def index
@posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json render :json => @posts
end
end
我想了解 respond_to 块的实际工作原理。什么类型的变量是格式?是格式对象的 .html 和 .json 方法吗?
的documentationActionController::MimeResponds::ClassMethods::respond_to
不回答问题。
【问题讨论】:
如果我可以链接到 ActionController::MimeResponds::ClassMethods::respond_to 的文档就好了,但是 api.rubyonrails.org 似乎不喜欢直接超链接... respond_to 结束调用(例如 blah.html、blah.json 等)并匹配指定的视图。其他响应可以是 XML、CSV 等等,具体取决于应用程序。 它如何“匹配指定的视图?” 我不认为扩展名(xml、html 等)映射到视图。如果您选择默认呈现(format.html
-- 无参数),它将使用约定(基于 URL 和 HTTP 动词)来选择视图(预期为 HTML)。此处指示响应者(格式)通过序列化为 json 来呈现以 .json 结尾的 URL,而不是使用视图和约定。
【参考方案1】:
我是 Ruby 新手,被困在相同的代码上。我挂断的部分比我在这里找到的一些答案更基本。这可能会或可能不会帮助某人。
respond_to
是超类 ActionController
上的一个方法。
它需要一个块,就像一个委托。块从do
到end
,|format|
作为块的参数。
respond_to 执行您的块,将 Responder 传递给 format
参数。
http://api.rubyonrails.org/v4.1/classes/ActionController/Responder.html
Responder
不包含.html
或.json
的方法,但我们还是调用了这些方法!这部分让我大吃一惊。
Ruby 有一个名为method_missing
的功能。如果您调用不存在的方法(如 json
或 html
),Ruby 会改为调用 method_missing
方法。
http://ruby-metaprogramming.rubylearning.com/html/ruby_metaprogramming_2.html
Responder
类使用其method_missing
作为一种注册。当我们调用“json”时,我们告诉它通过序列化为 json 来响应带有 .json 扩展名的请求。我们需要不带参数地调用html
,告诉它以默认方式(使用约定和视图)处理 .html 请求。
可以这样写(使用类似 JS 的伪代码):
// get an instance to a responder from the base class
var format = get_responder()
// register html to render in the default way
// (by way of the views and conventions)
format.register('html')
// register json as well. the argument to .json is the second
// argument to method_missing ('json' is the first), which contains
// optional ways to configure the response. In this case, serialize as json.
format.register('json', renderOptions)
这部分让我很困惑。我仍然觉得它不直观。 Ruby 似乎相当多地使用了这种技术。整个类 (responder
) 成为方法实现。为了利用method_missing
,我们需要一个类的实例,所以我们必须传递一个回调,他们将类方法对象传递给该回调。对于使用类 C 语言编码 20 年的人来说,这对我来说是非常落后和不直观的。不是说不好!但这是很多具有这种背景的人需要了解的事情,我认为这可能是 OP 所追求的。
附言请注意,在 RoR 4.2 中,respond_to
被提取到 responders gem 中。
【讨论】:
谢谢克雷格,这个链接实际上也有很多有用的信息,我没有意识到method_missing
有多少可能,考虑到你可以传递它参数和 一个块!
解释在 Responder 类中使用 method_missing() 作为注册机制的最佳答案!我也非常对这段代码感到困惑。
Rails 6 脚手架生成器似乎在控制器中生成带有 respond_to
的代码,而 Gemfile 中没有响应者 gem。也许关于 respond_to
被提取到响应者 gem 中的部分已经改变了?【参考方案2】:
这是一个利用 Rails 辅助方法的 Ruby 代码块。如果你还不熟悉块,你会在 Ruby 中看到很多。
respond_to
是附加到 Controller 类(或者更确切地说,它的超类)的 Rails 辅助方法。它引用将发送到视图(将发送到浏览器)的响应。
您示例中的块是格式化数据 - 通过在块中传递一个“格式”参数 - 每当浏览器请求 html 或 json 数据时从控制器发送到视图。
如果您在本地计算机上并且设置了 Post 脚手架,则可以转到 http://localhost:3000/posts
,您将看到所有 html 格式的帖子。但是,如果您输入:http://localhost:3000/posts.json
,那么您将在从服务器发送的 json 对象中看到您的所有帖子。
这对于制作需要从服务器来回传递 json 的 javascript 繁重的应用程序非常方便。如果您愿意,您可以轻松地在 Rails 后端创建一个 json api,并且只传递一个视图——比如 Post 控制器的索引视图。然后,您可以使用 Jquery 或 Backbone(或两者)之类的 javascript 库来操作数据并创建自己的界面。这些被称为异步 UI,并且它们变得非常流行(Gmail 就是其中之一)。它们速度非常快,并为最终用户提供了更像桌面的网络体验。当然,这只是格式化数据的优势之一。
Rails 3 的写法是这样的:
class PostsController < ApplicationController
# GET /posts
# GET /posts.xml
respond_to :html, :xml, :json
def index
@posts = Post.all
respond_with(@posts)
end
#
# All your other REST methods
#
end
通过将respond_to :html, :xml, :json
放在类的顶部,您可以声明希望控制器发送到视图的所有格式。
那么,在控制器方法中,你所要做的就是respond_with(@whatever_object_you_have)
与 Rails 自动生成的代码相比,它只是稍微简化了您的代码。
如果您想了解其内部工作原理...
据我了解,Rails 会内省对象以确定实际格式。 'format' 变量值基于这种自省。 Rails 可以用一点点信息做很多事情。你会惊讶于一个简单的@post 或 :post 会走多远。
例如,如果我有一个如下所示的 _user.html.erb 部分文件:
_user.html.erb
<li>
<%= link_to user.name, user %>
</li>
然后,仅此在我的索引视图中就会让 Rails 知道它需要找到“用户”部分并遍历所有“用户”对象:
index.html.erb
<ul class="users">
<%= render @users %>
</ul>
会让 Rails 知道它需要找到“用户”部分并遍历所有“用户”对象:
您可能会发现这篇博文很有用:http://archives.ryandaigle.com/articles/2009/8/6/what-s-new-in-edge-rails-cleaner-restful-controllers-w-respond_with
您也可以细读来源:https://github.com/rails/rails
【讨论】:
关于 rails3 方式的好技巧。我仍在尝试了解 respond_to 块的底部,以及块参数 |format| 的含义。通过了。 很好的答案,但没有具体说明传递到块中的格式变量。在给出的示例中,有 format.html 和 format.json - 这两个都传递给 respond_to,然后 respond_to 决定如何处理它们?respond_to
和 respond_with
是什么时候引入的?我正在使用 rails 2.3.5,我收到了 NoMethodError (undefined method respond_to)
【参考方案3】:
据我所知,respond_to 是附加到 ActionController 的方法,因此您可以在每个控制器中使用它,因为它们都继承自 ActionController。这是 Rails 的 respond_to 方法:
def respond_to(&block)
responder = Responder.new(self)
block.call(responder)
responder.respond
end
你正在传递一个block,就像我在这里展示的那样:
respond_to <<**BEGINNING OF THE BLOCK**>> do |format|
format.html
format.xml render :xml => @whatever
end <<**END OF THE BLOCK**>>
|format| 部分是块所期望的参数,因此我们可以在 respond_to 方法中使用它。如何?
好吧,如果你注意到我们在 respond_to 方法中传递带有前缀 & 的块,我们这样做是为了将该块视为 Proc。由于参数具有“.xml”、“.html”,我们可以将其用作要调用的方法。
我们在 respond_to 类中所做的基本上就是调用方法“.html, .xml, .json”到 Responder 类的一个实例。
【讨论】:
api 文档中的 respond_to 源与您包含的源不同,这让我很失望。您的 sn-p 让我更清楚地知道,格式块参数正在传递一个 Responder 对象。 Responder 文档似乎回答了这个问题,现在阅读。【参考方案4】:我想了解 respond_to 块的实际工作原理。什么 变量的类型是格式?是 .html 和 .json 格式的方法吗 对象?
要了解format
是什么,您可以先查看respond_to
的源代码,但很快您会发现真正需要查看的是retrieve_response_from_mimes 的代码。
从这里,您会看到传递给respond_to
(在您的代码中)的块实际上是通过Collector 的实例调用和传递的(在块中被称为format
) . Collector 基本上根据 mime types rails 所知道的信息生成方法(我相信在 Rails 启动时)。
所以,是的,.html
和 .json
是在收集器(又名format
)类上定义(在运行时)的方法。
【讨论】:
【参考方案5】:响应者注册背后的元编程(参见 Parched Squid 的回答)还允许您执行以下漂亮的操作:
def index
@posts = Post.all
respond_to do |format|
format.html # index.html.erb
format.json render :json => @posts
format.csv render :csv => @posts
format.js
end
end
当您访问 /posts.csv 时,csv 行将导致在每个帖子上调用 to_csv。这使得从您的 Rails 站点将数据导出为 CSV(或任何其他格式)变得容易。
js 行将导致 javascript 文件 /posts.js(或 /posts.js.coffee)被渲染/执行。我发现这是一种使用 jQuery UI 弹出窗口创建支持 Ajax 的网站的轻量级方法。
【讨论】:
【参考方案6】:这有点过时了,Ryan Bigg 在这里做了很好的解释:
http://ryanbigg.com/2009/04/how-rails-works-2-mime-types-respond_to
事实上,它可能比您想要的更详细。事实证明,幕后发生了很多事情,包括需要了解 MIME 类型是如何加载的。
【讨论】:
【参考方案7】:格式是什么类型的变量?
从 java POV 来看,格式是匿名接口的实现。这个接口有一个为每个 mime 类型命名的方法。当您调用其中一种方法(向其传递一个块)时,如果 rails 认为用户想要该内容类型,那么它将调用您的块。
当然,转折点在于这个匿名粘合对象实际上并没有实现接口——它动态地捕获方法调用,并确定它是否是它所知道的 mime 类型的名称。
就个人而言,我认为这看起来很奇怪:您传入的块是执行的。传递格式标签和块的哈希对我来说更有意义。但是 - 这似乎是它在 RoR 中的完成方式。
【讨论】:
【参考方案8】:“格式”是您的响应类型。例如,可以是 json 或 html。这是您的访问者将收到的输出格式。
【讨论】:
【参考方案9】:您还应该注意一件事 - MIME。
如果您需要使用 MIME 类型并且默认不支持,您可以在 config/initializers/mime_types.rb 中注册自己的处理程序:
Mime::Type.register "text/markdown", :markdown
【讨论】:
以上是关于Rails:respond_to 块是如何工作的?的主要内容,如果未能解决你的问题,请参考以下文章
Rails 5 API 控制器中未定义的实例方法“respond_to”
Rails 中的 respond_to 出现未知格式错误。正确尝试实施轮询更新
无法使用 Attachinary 在 Heroku 上运行 Rails 控制台:未定义方法 `respond_to' for Attachinary::CorsController:Class