跳轉至內容 跳轉至搜尋
命名空間
方法
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

變數也支援格式常見的任何/全部區塊。

對於內嵌與區塊語法都能運作

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

您也可以設定一個 variant 陣列

request.variant = [:tablet, :phone]

這的運作方式和格式與 MIME 型態協商類似。如果沒有宣告:平板電腦 variant,將會使用:手機 variant

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