Rails:response_to块如何工作?


210

我正在阅读Rails入门指南,并对6.7节感到困惑。生成支架后,我在控制器中找到以下自动生成的块:

def index
  @posts = Post.all

  respond_to do |format|
    format.html  # index.html.erb
    format.json  { render :json => @posts }
  end
end

我想了解response_to块实际上是如何工作的。格式是什么类型的变量?是格式对象的.html和.json方法吗?该文件

ActionController::MimeResponds::ClassMethods::respond_to

没有回答问题。


如果可以链接到ActionController :: MimeResponds :: ClassMethods :: respond_to的文档,但api.rubyonrails.org似乎不喜欢直接超链接,那就很好了
Cole

response_to结束调用(例如blah.html,blah.json等)并匹配指定的视图。其他响应可以是XML,CSV等等,取决于应用程序。
ScottJShea

5
它如何“匹配指定的视图”?
科尔

我不认为扩展名(xml,html等)映射到视图。如果选择默认呈现(format.html无参数),它将使用约定(基于URL和HTTP动词)选择视图(预期为HTML)。此处指示响应者(格式)通过序列化为json来呈现以.json结尾的URL,而不是使用视图和约定。
Craig Celeste

Answers:


188

我是Ruby的新手,却陷入了相同的代码。我挂断的部分比我在这里找到的一些答案更基本。这可能会或可能不会帮助某人。

  • respond_to是超类上的方法ActionController
  • 它需要一个块,就像一个委托。块从do直到end|format|作为该块的参数。
  • response_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所追求的。

ps注意,在RoR 4.2 respond_to中已将其提取到响应者 gem中。


谢谢Craig,该链接实际上也有大量有用的信息method_missing,考虑到您可以传递参数一个代码块,我还没有意识到用它可以实现的功能!
Aditya MP 2016年

2
解释method_missing()作为Responder类中的注册机制的最佳答案!我对这段代码也困惑。
艾伦·

1
Rails 6支架生成器似乎respond_to在控制器中生成代码,而Gemfile中没有响应者gem。也许respond_to被提取到响应者宝库中的位已更改?
卡西姆

106

这是利用Rails辅助方法的Ruby代码块。如果您还不熟悉块,那么您将在Ruby中看到很多。

respond_to是附加到Controller类(或更确切地说,其父类)的Rails帮助器方法。它引用了将发送到View(将发送到浏览器)的响应。

您的示例中的块正在格式化数据-通过在块中传递“格式”参数-每当浏览器请求html或json数据时都将从控制器发送到视图。

如果您在本地计算机上,并且已设置Post脚手架,则可以转到http://localhost:3000/posts,您将看到所有html格式的帖子。但是,如果您输入this http://localhost:3000/posts.json:,那么您将在服务器发送的json对象中看到所有帖子。

这对于制作需要从服务器来回传递json的JavaScript繁重的应用程序非常方便。如果您愿意,可以在rails后端轻松创建一个json api,并且只传递一个视图-例如Post控制器的索引视图。然后,您可以使用JqueryBackbone(或两者)之类的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类的顶部,可以声明希望控制器发送到视图的所有格式。

然后,在控制器方法中,您所要做的就是response_with(@whatever_object_you_have)

它只是比Rails自动生成的代码简化了一点。

如果您想了解其内部工作原理 ...

据我了解,Rails会对这些对象进行内省,以确定实际的格式。“格式”变量值基于此自省。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


1
在rails3上不错的提示。我仍在尝试进入response_to块的底部,以及块参数| format |的内容。被通过。
科尔

4
好的答案,但是没有说关于传递到块中的format变量的任何具体信息。在给定的示例中,有format.html和format.json-两者都传递给response_to,然后response_to决定如何处理它们吗?
安东尼

当被respond_torespond_with出台?我使用的是Rails 2.3.5,我得到的是NoMethodError (undefined method respond_to)
2014年

10

据我所知,response_to是ActionController附带的一种方法,因此您可以在每个控制器中使用它,因为它们全部都继承自ActionController。这是Rails的response_to方法:

def respond_to(&block)
  responder = Responder.new(self)
  block.call(responder)
  responder.respond
end

您正在传递一个,就像我在这里显示的那样:

respond_to <<**BEGINNING OF THE BLOCK**>> do |format|
  format.html
  format.xml  { render :xml => @whatever }
end <<**END OF THE BLOCK**>>

|格式| part是该块所期望的参数,因此可以在respond_to方法内部使用它。怎么样?

好吧,如果您注意到我们在response_to方法中传递了带有前缀&的块,则可以将该块视为Proc。由于参数具有“ .xml”,“。html”,因此我们可以将其用作要调用的方法。

我们基本上在react_to类中所做的是调用方法“ .html,.xml,.json”到Responder类的实例。


1
api文档中response_to的源与您包括的源不同,这使我失望。您的代码片段使我更清楚地知道format块参数正在传递给Responder对象。Responder文档似乎可以回答该问题,请立即阅读。
科尔

7

我想了解respond_to块实际上是如何工作的。格式是什么类型的变量?是格式对象的.html和.json方法吗?

为了了解什么format,您可以先查看的源respond_to,但很快您会发现真正需要查看的是retrieve_response_from_mimes的代码。

从这里,您将看到传递到respond_to(在您的代码中)的块实际上是通过Collector实例(在该块内被称为format)调用并传递的。Collector基本上根据Rails知道的MIME类型来生成方法(我相信是在Rails的启动阶段)。

因此,是的.htmland .json方法是在收集器(aka format)类上定义的(在运行时)方法。


2

响应者注册背后的元编程(请参阅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的站点的轻巧方式。


1

格式是什么类型的变量?

从Java POV来看,格式是匿名接口的实现。此接口有一个为每种mime类型命名的方法。当您调用这些方法之一(将其传递给块)时,如果Rails认为用户需要该内容类型,则它将调用您的块。

当然,这种变化是,该匿名粘合对象实际上没有实现接口-它动态捕获方法调用并确定其是否知道它的mime类型的名称。

我个人认为这看起来很奇怪:您传入的代码块已执行。对我来说,传递格式标签和块的哈希值更有意义。但是-这似乎就是RoR的工作方式。




0

您还应该注意一件事-MIME。

如果需要使用MIME类型,并且默认情况下不支持该类型,则可以在config / initializers / mime_types.rb中注册自己的处理程序:

Mime::Type.register "text/markdown", :markdown

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.