класс ActionController::Responder
Отвечает за предоставление ресурса для различных запросов с разными типами контента, обычно в зависимости от HTTP-метода. Ответчик срабатывает при вызове respond_with. Простейший случай для изучения — это запрос GET:
class PeopleController < ApplicationController
respond_to :html, :xml, :json
def index
@people = Person.all
respond_with(@people)
end
end
При поступлении запроса, например, для получения ответа в формате XML, происходят три шага:
1) the responder searches for a template at people/index.xml; 2) if the template is not available, it will invoke <code>#to_xml</code> on the given resource; 3) if the responder does not <code>respond_to :to_xml</code>, call <code>#to_format</code> on it.
Встроенная семантика HTTP-методов
Стандартный ответчик Rails содержит семантику для каждого HTTP-метода. В зависимости от типа контента, метода и состояния ресурса, он будет вести себя по-разному.
Используя стандартный ответчик Rails, запрос POST для создания объекта можно записать как:
def create @user = User.new(params[:user]) flash[:notice] = 'User was successfully created.' if @user.save respond_with(@user) end
Что точно так же, как:
def create
@user = User.new(params[:user])
respond_to do |format|
if @user.save
flash[:notice] = 'User was successfully created.'
format.html { redirect_to(@user) }
format.xml { render xml: @user, status: :created, location: @user }
else
format.html { render action: "new" }
format.xml { render xml: @user.errors, status: :unprocessable_entity }
end
end
end
То же самое происходит для запросов PATCH/PUT и DELETE.
Вложенные ресурсы
Вы можете передавать вложенные ресурсы так же, как делаете в form_for и polymorphic_url. Представьте, что у проекта много задач. Действие create для контроллера TasksController будет выглядеть так:
def create @project = Project.find(params[:project_id]) @task = @project.tasks.build(params[:task]) flash[:notice] = 'Task was successfully created.' if @task.save respond_with(@project, @task) end
Передача нескольких ресурсов гарантирует, что ответчик будет перенаправлять на project_task_url вместо task_url.
Для именованных и одиночных ресурсов требуется передать символ, как в полиморфных URL. Если у проекта один менеджер, который имеет много задач, вызов должен быть таким:
respond_with(@project, :manager, @task)
Обратите внимание, что если вы передаете массив, он будет обрабатываться как коллекция, поэтому следующее не эквивалентно:
respond_with [@project, :manager, @task]
Настраиваемые параметры
respond_with также позволяет передавать параметры, которые передаются в вызов render. Эти параметры применяются только для успешных сценариев. Например, вы можете сделать следующее в методе create выше:
def create @project = Project.find(params[:project_id]) @task = @project.tasks.build(params[:task]) flash[:notice] = 'Task was successfully created.' if @task.save respond_with(@project, @task, status: 201) end
Это вернёт статус 201, если задача была успешно сохранена. В противном случае, он просто проигнорирует заданные параметры и вернёт статус 422 и ошибки ресурса. Вы также можете переопределить место перенаправления:
respond_with(@project, location: root_path)
Чтобы настроить сценарий ошибки, вы можете передать блок в respond_with:
def create
@project = Project.find(params[:project_id])
@task = @project.tasks.build(params[:task])
respond_with(@project, @task, status: 201) do |format|
if @task.save
flash[:notice] = 'Task was successfully created.'
else
format.html { render "some_special_template" }
end
end
end
Использование respond_with с блоком следует той же синтаксической конструкции, что и respond_to.
Константы
- DEFAULT_ACTIONS_FOR_VERBS
Атрибуты
Публичные методы класса
Инициализирует новый ответчик и вызывает соответствующий формат. Если формат не определен, вызов to_format.
# File actionpack/lib/action_controller/metal/responder.rb, line 150 def self.call(*args) new(*args).respond end
# File actionpack/lib/action_controller/metal/responder.rb, line 129
def initialize(controller, resources, options={})
@controller = controller
@request = @controller.request
@format = @controller.formats.first
@resource = resources.last
@resources = resources
@options = options
@action = options.delete(:action)
@default_response = options.delete(:default_response)
end Публичные методы экземпляра
Основная точка входа для ответчика, ответственного за отправку в соответствующий формат.
# File actionpack/lib/action_controller/metal/responder.rb, line 156
def respond
method = "to_#{format}"
respond_to?(method) ? send(method) : to_format
end Все остальные форматы следуют процедуре, описанной ниже. Сначала мы пытаемся отобразить шаблон, если шаблон недоступен, мы проверяем, отвечает ли ресурс на :to_format и отображаем его.
# File actionpack/lib/action_controller/metal/responder.rb, line 179
def to_format
if get? || !has_errors? || response_overridden?
default_render
else
display_errors
end
rescue ActionView::MissingTemplate => e
api_behavior(e)
end HTML-формат не отображает ресурс, он всегда пытается отобразить шаблон.
# File actionpack/lib/action_controller/metal/responder.rb, line 164 def to_html default_render rescue ActionView::MissingTemplate => e navigation_behavior(e) end
#to_js просто пытается отобразить шаблон. Если шаблон не найден, выдает ошибку.
# File actionpack/lib/action_controller/metal/responder.rb, line 171 def to_js default_render end
Защищенные методы экземпляра
Это общее поведение для форматов, связанных с API, таких как :xml и :json.
# File actionpack/lib/action_controller/metal/responder.rb, line 203
def api_behavior(error)
raise error unless resourceful?
raise MissingRenderer.new(format) unless has_renderer?
if get?
display resource
elsif post?
display resource, :status => :created, :location => api_location
else
head :no_content
end
end По умолчанию отображает действие :edit для HTML-запросов с ошибками, если только глагол не был POST.
# File actionpack/lib/action_controller/metal/responder.rb, line 281 def default_action @action ||= DEFAULT_ACTIONS_FOR_VERBS[request.request_method_symbol] end
Если был задан блок ответа, используйте его, иначе вызовите render в контроллере.
# File actionpack/lib/action_controller/metal/responder.rb, line 234
def default_render
if @default_response
@default_response.call(options)
else
controller.default_render(options)
end
end Display — это просто сокращение для отображения ресурса с текущим форматом.
display @user, status: :ok
Для XML-запросов это эквивалентно:
render xml: @user, status: :ok
Используются также параметры, отправленные пользователем:
respond_with(@user, status: :created) display(@user, status: :ok)
Результат:
render xml: @user, status: :created
# File actionpack/lib/action_controller/metal/responder.rb, line 259
def display(resource, given_options={})
controller.render given_options.merge!(options).merge!(format => resource)
end # File actionpack/lib/action_controller/metal/responder.rb, line 263 def display_errors controller.render format => resource_errors, :status => :unprocessable_entity end
Проверить, есть ли у ресурса ошибки.
# File actionpack/lib/action_controller/metal/responder.rb, line 269 def has_errors? resource.respond_to?(:errors) && !resource.errors.empty? end
Проверить, доступен ли необходимый Renderer
# File actionpack/lib/action_controller/metal/responder.rb, line 274 def has_renderer? Renderers::RENDERERS.include?(format) end
# File actionpack/lib/action_controller/metal/responder.rb, line 289
def json_resource_errors
{:errors => resource.errors}
end Это общее поведение для форматов, связанных с просмотром, таких как :html, :iphone и так далее.
# File actionpack/lib/action_controller/metal/responder.rb, line 285
def resource_errors
respond_to?("#{format}_resource_errors", true) ? send("#{format}_resource_errors") : resource.errors
end Возвращает местоположение ресурса, извлекая его из параметров или возвращая массив ресурсов.
# File actionpack/lib/action_controller/metal/responder.rb, line 225 def resource_location options[:location] || resources end
Проверяет, отвечает ли ресурс на текущий формат или нет.
# File actionpack/lib/action_controller/metal/responder.rb, line 218
def resourceful?
resource.respond_to?("to_#{format}")
end # File actionpack/lib/action_controller/metal/responder.rb, line 293 def response_overridden? @default_response.present? end
© 2004–2016 David Heinemeier Hansson
Licensed under the MIT License.