модуль 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
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>...</name>
<company>
<name>...</name>
</company>
</person> И так (в кодировке 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
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 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–2019 David Heinemeier Hansson
Licensed under the MIT License.