модуль ActionController::MimeResponds
Публичные методы экземпляра
# File actionpack/lib/action_controller/metal/mime_responds.rb, line 201
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.