модуль ActionController::MimeResponds
Публичные методы экземпляров
# File actionpack/lib/action_controller/metal/mime_responds.rb, line 191
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)
_process_format(format)
_set_rendered_content_type format
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/jpg", :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
Форматы могут иметь разные варианты.
Вариант запроса — специализация формата запроса, например :tablet, :phone, или :desktop.
Часто нам нужно отображать разные шаблоны html/json/xml для телефонов, планшетов и настольных компьютеров. Варианты упрощают это.
Вы можете установить вариант в before_action:
request.variant = :tablet if request.user_agent =~ /iPad/
Обрабатывайте варианты в действии так же, как обрабатываете форматы:
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–2018 David Heinemeier Hansson
Licensed under the MIT License.