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 方法吗?

的documentation

ActionController::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 上的一个方法。 它需要一个块,就像一个委托。块从doend|format| 作为块的参数。 respond_to 执行您的块,将 Responder 传递给 format 参数。

http://api.rubyonrails.org/v4.1/classes/ActionController/Responder.html

Responder 不包含.html.json 的方法,但我们还是调用了这些方法!这部分让我大吃一惊。 Ruby 有一个名为method_missing 的功能。如果您调用不存在的方法(如 jsonhtml),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_torespond_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 重定向不起作用

Rails 中的 respond_to 出现未知格式错误。正确尝试实施轮询更新

无法使用 Attachinary 在 Heroku 上运行 Rails 控制台:未定义方法 `respond_to' for Attachinary::CorsController:Class

Rails:respond_to js 不使用文本呈现是一个字符串

如何限制 Rails 路由文件中的资源格式