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