Spec-Zone.ru › Ruby on Rails 7.2

модуль ActionController::MimeResponds

Общедоступные методы экземпляров

respond_to(*mimes) { |collector| ... } Показать исходный код
# 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

Без поддержки веб-сервисов действие, которое собирает данные для отображения списка людей, может выглядеть примерно так:

def index
  @people = Person.all
end

Это действие неявно отвечает на все форматы, но форматы также можно перечислить явно:

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

Вот то же действие с интегрированной поддержкой веб-сервисов:

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, отправленного клиентом.)

Предположим, у вас есть действие, которое добавляет нового человека, при необходимости создавая его компанию (по имени), если она ещё не существует. Без веб-сервисов это может выглядеть так:

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

  redirect_to(person_list_url)
end

Вот то же действие с интегрированной поддержкой веб-сервисов:

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-документ (если выполняется запрос веб-сервиса) может содержать только один корневой узел. Поэтому мы должны переупорядочить вещи, чтобы запрос выглядел так (в кодировке 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

© 2004–2021 David Heinemeier Hansson
Licensed under the MIT License.

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API