跳至内容 跳至搜索
命名空间
方法
R

实例公共方法

respond_to(*mimes)

在没有 Web 服务支持的情况下,收集用于显示人员列表数据的操作可能看起来像这样

def index
  @people = Person.all
end

该操作隐式响应所有格式,但格式也可以显式枚举

def index
  @people = Person.all
  respond_to :html, :js
end

这是同一个操作,集成了 Web 服务支持

def index
  @people = Person.all

  respond_to do |format|
    format.html
    format.js
    format.xml { render xml: @people }
  end
end

这意味着,“如果客户端希望在响应此操作时获取 HTML 或 JS,则像以前一样响应,但如果客户端希望获取 XML,则以 XML 格式返回人员列表。” (Rails 从客户端提交的 HTTP Accept 标头确定所需的响应格式。)

假设您有一个操作可以添加新人员,可以选择创建他们的公司(按名称),如果公司不存在,则在没有 Web 服务的情况下,它可能看起来像这样

def create
  @company = Company.find_or_create_by(name: params[:company][:name])
  @person  = @company.people.create(params[:person])

  redirect_to(person_list_url)
end

这是同一个操作,集成了 Web 服务支持

def create
  company  = params[:person].delete(:company)
  @company = Company.find_or_create_by(name: company[:name])
  @person  = @company.people.create(params[:person])

  respond_to do |format|
    format.html { redirect_to(person_list_url) }
    format.js
    format.xml  { render xml: @person.to_xml(include: @company) }
  end
end

如果客户端想要 HTML,我们只需将他们重定向回人员列表。如果他们想要 JavaScript,那么这是一个 Ajax 请求,我们渲染与该操作相关的 JavaScript 模板。最后,如果客户端想要 XML,我们渲染创建的人员为 XML,但有一个小技巧:我们还将该人员的公司包含在渲染的 XML 中,因此您会得到类似于以下内容:

<person>
  <id>...</id>
  ...
  <company>
    <id>...</id>
    <name>...</name>
    ...
  </company>
</person>

但是,请注意该操作顶部的额外部分

company  = params[:person].delete(:company)
@company = Company.find_or_create_by(name: company[:name])

这是因为传入的 XML 文档(如果 Web 服务请求正在处理中)只能包含单个根节点。因此,我们必须重新排列事物,以便请求看起来像这样(URL 编码):

person[name]=...&person[company][name]=...&...

以及像这样(XML 编码):

<person>
  <name>...</name>
  <company>
    <name>...</name>
  </company>
</person>

换句话说,我们对请求进行了修改,以便它对单个实体的人员进行操作。然后,在操作中,我们从请求中提取公司数据,查找或创建公司,然后使用剩余数据创建新人员。

请注意,您可以定义自己的 XML 参数解析器,它允许您在单个请求中描述多个实体(例如,通过将它们全部包装在单个根节点中),但如果您只是遵循流程并接受 Rails 的默认设置,生活会变得容易得多。

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

Mime::Type.register "image/jpeg", :jpg

respond_to 还允许您使用 any 为不同的格式指定一个公共块

def index
  @people = Person.all

  respond_to do |format|
    format.html
    format.any(:xml, :json) { render request.format.to_sym => @people }
  end
end

在上面的示例中,如果格式为 xml,它将渲染

render xml: @people

或者如果格式为 json

render json: @people

any 也可以不带参数使用,在这种情况下,它将用于用户请求的任何格式

respond_to do |format|
  format.html
  format.any { redirect_to support_path }
end

格式可以有不同的变体。

请求变体是请求格式的专门化,例如 :tablet:phone:desktop

我们经常希望为手机、平板电脑和台式机浏览器渲染不同的 html/json/xml 模板。变体使这变得很容易。

您可以在 before_action 中设置变体

request.variant = :tablet if /iPad/.match?(request.user_agent)

在操作中像响应格式一样响应变体

respond_to do |format|
  format.html do |variant|
    variant.tablet # renders app/views/projects/show.html+tablet.erb
    variant.phone { extra_setup; render ... }
    variant.none  { special_setup } # executed only if there is no variant set
  end
end

为每种格式和变体提供单独的模板

app/views/projects/show.html.erb
app/views/projects/show.html+tablet.erb
app/views/projects/show.html+phone.erb

当您不在格式内共享任何代码时,可以使用内联语法简化变体的定义

respond_to do |format|
  format.js         { render "trash" }
  format.html.phone { redirect_to progress_path }
  format.html.none  { render "trash" }
end

变体还支持格式具有的公共 any/all 块。

它适用于内联

respond_to do |format|
  format.html.any   { render html: "any"   }
  format.html.phone { render html: "phone" }
end

和块语法

respond_to do |format|
  format.html do |variant|
    variant.any(:tablet, :phablet){ render html: "any" }
    variant.phone { render html: "phone" }
  end
end

您还可以设置一个变体数组

request.variant = [:tablet, :phone]

这将类似于格式和 MIME 类型的协商工作。如果没有声明 :tablet 变体,则将使用 :phone 变体

respond_to do |format|
  format.html.none
  format.html.phone # this gets rendered
end
# File actionpack/lib/action_controller/metal/mime_responds.rb, line 211
def respond_to(*mimes)
  raise ArgumentError, "respond_to takes either types or a block, never both" if mimes.any? && block_given?

  collector = Collector.new(mimes, request.variant)
  yield collector if block_given?

  if format = collector.negotiate_format(request)
    if media_type && media_type != format
      raise ActionController::RespondToMismatchError
    end
    _process_format(format)
    _set_rendered_content_type(format) unless collector.any_response?
    response = collector.response
    response.call if response
  else
    raise ActionController::UnknownFormat
  end
end