По этому руководству вы изучите, как работают контроллеры, и как они вписываются в цикл запроса к вашему приложению.
После его прочтения, вы узнаете, как:
Action Controller это C в аббревиатуре MVC. После того, как роутер определит, какой контроллер использовать для обработки запроса, контроллер ответственен за осмысление запроса и генерацию подходящего ответа. К счастью, Action Controller делает за вас большую часть грязной работы и использует элегантные соглашения, чтобы сделать это по возможности максимально просто.
Для большинства приложений, основанных на RESTful, контроллер получает запрос (это невидимо для вас, как для разработчика), извлекает или сохраняет данные в модели и использует вью для создания результирующего HTML. Если контроллеру необходимо работать немного по-другому, не проблема, это всего лишь наиболее распространенный способ работы контроллера.
Таким образом, контроллер можно рассматривать как посредника между моделями и вью. Он делает данные модели доступными вью, так что она может отображать эти данные пользователю, и он сохраняет или обновляет данные от пользователя в модель.
Более детально о процессе маршрутизации смотрите Роутинг в Rails.
Соглашение по именованию контроллеров в Rails устанавливает предпочтение множественного числа в последнем слове имени контроллера, хотя строго это не требуется (например, ApplicationController
). К примеру, ClientsController
более предпочтителен, чем ClientController
, SiteAdminsController
более предпочтителен, чем SiteAdminController
или SitesAdminsController
, и так далее.
Следование этому соглашению позволяет вам использовать генераторы маршрутов по умолчанию (например, resources
и т.п.) без необходимости определять каждый :path
или :controller
, и сохраняет последовательным использование хелперов именованных путей во всем вашем приложении. Подробнее смотрите в руководстве Макеты и рендеринг в Rails.
Соглашение по именованию контроллеров отличается от соглашения по именованию моделей, которые, как ожидается, будут именоваться в единственном числе.
Контроллер - это класс Ruby, унаследованный от ApplicationController
и содержащий методы, как и любой другой класс. Когда ваше приложение получает запрос, роутинг определяет, какой контроллер и экшн нужно запустить, затем Rails создает экземпляр этого контроллера и запускает метод с именем, как у экшна.
class ClientsController < ApplicationController
def new
end
end
В качестве примера, если пользователь перейдет на /clients/new
в приложении, чтобы добавить нового клиента, Rails создаст экземпляр ClientsController
и вызовет метод new
. Отметьте, что пустой метод из вышеприведенного примера будет прекрасно работать, так как Rails по умолчанию отрендерит вью new.html.erb
, если в экшне не будет указано иное. При создании нового Client
, метод new
может сделать переменную экземпляра @client
доступной во вью:
def new
@client = Client.new
end
Руководство Макеты и рендеринг в Rails объясняет это более детально.
ApplicationController
унаследован от ActionController::Base
, который определяет несколько полезных методов. Это руководство раскроет часть из них, но если вы любопытны, можете увидеть их все в документации по API.
Только public методы могут быть вызваны как экшны. Хорошей практикой является уменьшение области видимости методов (при помощи private
или protected
), не предназначенных быть экшнами, таких как вспомогательные методы и фильтры.
Некоторые имена методов зарезервированы в Controller. Случайное переопределение их как экшны, или даже как вспомогательные методы, может привести к SystemStackError
. Если вы ограничиваете свои контроллеры иметь только RESTful экшны в [Ресурсном роутинге][], об этом можно не беспокоиться.
Если вы должны использовать зарезервированные методы в качестве имени экшна, это можно обойти, используя пользовательский маршрут, для направления зарезервированного имени метода на не зарезервированный метод экшна.
Возможно, вы хотите получить доступ к данным, посланным пользователем, или к другим параметрам в экшнах вашего контроллера. Существует два типа параметров, доступных в веб-приложениях. Первый - это параметры, посланные как часть URL, называемые параметрами строки запроса. Строка запроса всегда следует после "?" в URL. Второй тип параметров обычно упоминается как данные POST. Эта информация обычно приходит из формы HTML, заполняемой пользователем. Эти параметры еще называют данными POST, так как могут быть посланы только как часть HTTP-запроса метода POST. Rails не делает каких-либо различий между строковыми параметрами и параметрами POST, и они оба доступны в хэше params
в вашем контроллере:
class ClientsController < ApplicationController
# Этот экшн использует параметры строки запроса, потому что он
# запускается HTTP-запросом метода GET, но это не влияет на
# то, как можно получить доступ к ним.
# URL для этого экшна выглядит как этот, запрашивающий список
# активированных клиентов: /clients?status=activated
def index
if params[:status] == "activated"
@clients = Client.activated
else
@clients = Client.inactivated
end
end
# Этот экшн использует параметры POST. Они, скорее всего, пришли от
# формы HTML, которую подтвердил пользователь. URL для этого
# RESTful запроса будет "/clients", и данные будут посланы
# как часть тела запроса.
def create
@client = Client.new(params[:client])
if @client.save
redirect_to @client
else
# Эта строчка переопределяет поведение рендеринга по умолчанию,
# который отрендерил бы вью "create".
render "new"
end
end
end
Хэш params
не ограничен одномерными ключами и значениями. Он может содержать вложенные массивы и хэши. Чтобы послать массив значений, добавьте пустую пару квадратных скобок "[]" к имени ключа:
GET /clients?ids[]=1&ids[]=2&ids[]=3
Фактический URL в этом примере будет перекодирован как "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3", так как "[" и "]" недопустимы в URL. В основном, вам не придется беспокоиться об этом, так как браузер позаботится об этом за вас, а Rails декодирует это обратно, когда получит, но если вы когда-нибудь будете отправлять эти запросы вручную, имейте это в виду.
Значение params[:ids]
теперь будет ["1", "2", "3"]
. Отметьте, что значения параметра всегда строковое; Rails не пытается угадать или предсказать тип.
Значения, такие как [nil]
или [nil, nil, ...]
в params
по умолчанию заменяются на []
по причине безопасности. Подробнее смотрите в руководстве Безопасность приложений на Rails.
Чтобы послать хэш, следует заключить имя ключа в скобки:
<form accept-charset="UTF-8" action="/clients" method="post">
<input type="text" name="client[name]" value="Acme" />
<input type="text" name="client[phone]" value="12345" />
<input type="text" name="client[address][postcode]" value="12345" />
<input type="text" name="client[address][city]" value="Carrot City" />
</form>
Когда эта форма будет подтверждена, значение params[:client]
будет { "name" => "Acme", "phone" => "12345", "address" => { "postcode" => "12345", "city" => "Carrot City" } }
. Обратите внимание на вложенный хэш в params[:client][:address]
.
Объект params
ведет себя как хэш, но позволяет взаимозаменяемо использовать символы и строки как ключи.
Если ваше приложение предоставляет API, вы, вероятно, будете принимать параметры в формате JSON. Если заголовок "Content-Type" вашего запроса установлен на "application/json", Rails автоматически загрузит ваши параметры в хэш params
, к которому вы сможете получить доступ обычным способом.
Так, к примеру, если вы пошлете такое содержимое JSON:
{ "company": { "name": "acme", "address": "123 Carrot Street" } }
Ваш контроллер будет получать params[:company]
как { "name" => "acme", "address" => "123 Carrot Street" }
.
Также, если включите config.wrap_parameters
в своем инициализаторе или вызовете wrap_parameters
в своем контроллере, можно безопасно опустить корневой элемент в параметре JSON. Параметры будут клонированы и обернуты в ключ, соответствующий по умолчанию имени вашего контроллера. Таким образом, вышеупомянутый запрос JSON может быть записан как:
{ "name": "acme", "address": "123 Carrot Street" }
И предположим, что мы посылаем данные в CompaniesController
, тогда он будет обернут в ключ :company
следующим образом:
{ name: "acme", address: "123 Carrot Street", company: { name: "acme", address: "123 Carrot Street" } }
Вы сможете настроить имя ключа или определенные параметры, которые вы хотите обернуть, ознакомившись с документацией по API.
Поддержка парсинга параметров XML была извлечена в гем actionpack-xml_parser
.
Хэш params
будет всегда содержать ключи :controller
и :action
, но следует использовать методы controller_name
и action_name
вместо них для доступа к этим значениям. Любой другой параметр, определенный роутингом, такой как :id
, также будет доступен. Например, рассмотрим перечень клиентов, где список может быть показан либо для активных, либо для неактивных клиентов. Мы можем добавить маршрут, перехватывающий параметр :status
в "красивом" URL:
get '/clients/:status', to: 'clients#index', foo: 'bar'
В этом случае, когда пользователь откроет URL /clients/active
, params[:status]
будет установлен в "active". Когда использован этот маршрут, params[:foo]
также будет установлен в "bar", как будто он был передан в строке запроса. Ваш контроллер также получит params[:action]
как "index" и params[:controller]
как "clients".
Параметры составного ключа содержат несколько значений в одном параметре. По этой причине нам нужно иметь возможность извлекать каждое значение и передавать их в Active Record. Для этого случая мы можем использовать метод extract_value
.
Рассмотрим следующий контроллер:
class BooksController < ApplicationController
def show
# Извлекаем значение составного ID из параметров URL.
id = params.extract_value(:id)
# Находим книгу по составному ID.
@book = Book.find(id)
# Используем поведение отрисовки по умолчанию для отрисовки вью show.
end
end
И следующий маршрут:
get '/books/:id', to: 'books#show'
Когда пользователь открывает URL /books/4_2
, контроллер извлекает составное значение ключа ["4", "2"]
и передает его в Book.find
для отрисовки нужной записи во вью. Метод extract_value
может использоваться для извлечения массивов из любых параметров с разделителями.
default_url_options
Можно установить глобальные параметры по умолчанию для генерации URL, определив в контроллере метод по имени default_url_options
. Этот метод должен возвращать хэш с желаемыми значениями по умолчанию, ключи которого должны быть символами:
class ApplicationController < ActionController::Base
def default_url_options
{ locale: I18n.locale }
end
end
Эти опции будут использованы как начальная точка при генерации URL, поэтому они могут быть переопределены опциями, переданными в url_for
.
Если определить default_url_options
в ApplicationController
, как это показано в вышеприведенном примере, эти значения по умолчанию будут использованы для генерации всех URL. Этот метод также может быть определен в одном отдельном контроллере, и в этом случае он влияет только на URL, сгенерированные в нем.
В данном запросе, на самом деле, метод не вызывается для каждого сгенерированного URL; для повышения производительности, возвращаемый хэш кэшируется, и метод выполняется не более одного раза за запрос.
С помощью сильных параметров (strong parameters) параметры Action Controller запрещены к использованию в массовых назначениях Active Model до тех пор, пока они не разрешены. Это означает, что нужно будет принять осознанное решение о том, какие атрибуты будут разрешены для массового обновления. Это лучший способ предотвратить случайную уязвимость, позволяющую пользователям обновлять чувствительные атрибуты модели.
Кроме того, параметры могут быть помечены как обязательные и будут проходить через предопределенные raise/rescue, что приведет к 400 Bad Request, если не будут переданы все обязательные параметры.
class PeopleController < ActionController::Base
# Это вызовет исключение ActiveModel::ForbiddenAttributesError,
# так как используется массовое назначение без явного шага permit.
def create
Person.create(params[:person])
end
# Это будет выполняться должным образом, пока в параметрах есть ключ person, иначе будет
# вызвано исключение ActionController::ParameterMissing, которое будет
# поймано в ActionController::Base и превращено в ошибку 400 Bad Request.
def update
person = current_account.people.find(params[:id])
person.update!(person_params)
redirect_to person
end
private
# Использование приватного метода для инкапсуляции разрешенных параметров -
# это всего лишь хороший паттерн, с помощью которого можно повторно
# использовать тот же самый список разрешений при создании и обновлении.
# Этот метод также можно адаптировать к проверке разрешенных атрибутов для
# каждого пользователя.
def person_params
params.require(:person).permit(:name, :age)
end
end
Вызов permit
подобный:
params.permit(:id)
разрешает указанный ключ (:id
) для включения, если он появится в params
и будет иметь разрешенное скалярное значение. В ином случае ключ будет отфильтрован, таким образом, массивы, хэши и любые другие объекты не смогут быть переданы.
Разрешенные скалярные типы следующие String
, Symbol
, NilClass
, Numeric
, TrueClass
, FalseClass
, Date
, Time
, DateTime
, StringIO
, IO
, ActionDispatch::Http::UploadedFile
и Rack::Test::UploadedFile
.
Чтобы объявить, что значение в params
должно быть массивом разрешенных скалярных значений, свяжите ключ с пустым массивом:
params.permit(id: [])
Иногда невозможно или неудобно объявлять валидные ключи параметров хэша или его внутреннюю структуру. Просто укажите пустой хэш:
params.permit(preferences: {})
но будьте осторожны, так это открывает возможность произвольного ввода. В этом случае permit
гарантирует, что значения в возвращаемой структуре являются разрешенными скалярными величинами и отфильтровывает все иное.
Чтобы разрешить полный хэш параметров, можно использовать метод permit!
:
params.require(:log_entry).permit!
Это помечает хэш параметров :log_entry
и любые вложенные хэши как разрешенные, и не проверяет разрешенные скалярные величины, принимается все. Следует соблюдать предельную осторожность при использовании permit!
, так как он позволит массовое назначение всех текущих и будущих атрибутов модели.
Также можно использовать permit
c вложенными параметрами, например:
params.permit(:name, { emails: [] },
friends: [ :name,
{ family: [ :name ], hobbies: [] }])
Это объявление разрешает атрибуты name
, emails
и friends
. Ожидается, что emails
будет массивом разрешенных скалярных значений, и что friends
будет массивом ресурсов с определенными атрибутами: у них будет атрибут name
(допустимо любое скалярное значение), атрибут hobbies
как массив разрешенных скалярных значений, и атрибут family
, который может иметь только name
(также допустимо любое скалярное значение).
Возможно вы захотите использовать разрешенные атрибуты в экшне new
. В этой связи возникает проблема, из-за которой нельзя использовать require
на корневом ключе, так как обычно он не существует при вызове new
:
# используя `fetch`, можно предоставить значение по умолчанию и использовать
# далее Strong Parameters API.
params.fetch(:blog, {}).permit(:title, :author)
Метод класса модели accepts_nested_attributes_for
позволяет обновлять и удалять связанные записи. Он основывается на параметрах id
и _destroy
:
# permit :id и :_destroy
params.require(:author).permit(:name, books_attributes: [:title, :id, :_destroy])
Хэши с числовыми ключами трактуются по-другому, и можно объявить атрибуты так, как будто они являются прямыми детьми. Такой тип параметров можно получить при использовании accepts_nested_attributes_for
в сочетании со связью has_many
:
# Чтобы разрешить следующие данные:
# {"book" => {"title" => "Some Book",
# "chapters_attributes" => { "1" => {"title" => "First Chapter"},
# "2" => {"title" => "Second Chapter"}}}}
params.require(:book).permit(:title, chapters_attributes: [:title])
Рассмотрим ситуацию, когда есть параметры, представляющие имя продукта, и хэш произвольных данных, связанных с этим продуктом, и нужно разрешить атрибут name продукта, а также весь хэш данных:
def product_params
params.require(:product).permit(:name, data: {})
end
Strong parameter API был разработан для наиболее общих вариантов использования. Это не панацея от всех ваших проблем белого списка. Однако можно легко смешивать API с вашим собственным кодом для адаптации к вашей ситуации.
В приложении есть сессия для каждого пользователя, в которой можно хранить небольшие объемы данных, которые будут персистентными между запросами. Сессия доступна только в контроллере и во вью, и может использовать один из нескольких механизмов хранения:
ActionDispatch::Session::CookieStore
- Хранит все на клиенте.
ActionDispatch::Session::CacheStore
- Хранит данные в кэше Rails.
ActionDispatch::Session::MemCacheStore
- Хранит данные в кластере memcached (эта устаревшая реализация, вместо нее рассмотрите использование CacheStore
).
ActionDispatch::Session::ActiveRecordStore
- Хранит данные в базе данных с использованием Active Record. (требует гем activerecord-session_store
).
Все хранилища сессии используют куки для хранения уникального ID каждой сессии (вы должны использовать куки, Rails не позволяет передавать ID сессии в URL, так как это менее безопасно).
В большинстве хранилищ этот ID используется для поиска данных сессии на сервере, в т.ч. в таблице базы данных. Имеется одно исключение, это дефолтное и рекомендуемое хранилище сессии - CookieStore - которое хранит все данные сессии в куки (ID остается по-прежнему доступным, если он нужен). Преимущества его заключаются в легкости, отсутствии необходимости настройки для нового приложения чтобы использовать сессию. Данные в куки криптографически подписаны, что делает их защищенными от взлома. И они также зашифрованы, таким образом любой получивший к ним доступ, не сможет прочитать их содержимое (Rails не примет их, если они были отредактированы).
CookieStore могут хранить около 4 Кбайт данных - намного меньше, чем остальные - но этого обычно хватает. Хранение большего количества данных в сессии не рекомендуется, вне зависимости от того, какое хранилище сессии используется в приложении. Особенно следует избегать хранения в сессии сложных объектов (такие как экземпляры модели), так как сервер может не собрать их между запросами, что приведет к ошибке.
Если пользовательские сессии не хранят критически важные данные или нет необходимости в ее сохранности на долгий период (скажем, если она используется только для флеш-сообщений), можно рассмотреть использование ActionDispatch::Session::CacheStore
. Он сохранит сессии с использованием реализации кэша, которая была настроена для приложения. Преимущество этого заключается в том, что для хранения сессий можно использовать существующую инфраструктуру кэширования без необходимости дополнительных настроек или администрирования. Недостатком, разумеется, является то, что сессии будут недолговечными и могут исчезнуть в любое время.
Читайте подробнее о хранении сессий в руководстве Безопасность приложений на Rails.
Если вы нуждаетесь в другом механизме хранения сессий, измените его в инициализаторе:
Rails.application.config.session_store :cache_store
Подробности смотрите в config.session_store
в руководстве по конфигурации.
Rails устанавливает ключ сессии (имя куки) при подписании данных сессии. Он также может быть изменен в инициализаторе:
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_your_app_session'
Можете также передать ключ :domain
и определить имя домена для куки:
# Be sure to restart your server when you modify this file.
Rails.application.config.session_store :cookie_store, key: '_your_app_session', domain: ".example.com"
Rails устанавливает (для CookieStore) секретный ключ, используемый для подписания данных сессии, в config/credentials.yml.enc
. Он может быть изменен с помощью bin/rails credentials:edit
.
# aws:
# access_key_id: 123
# secret_access_key: 345
# Used as the base secret for all MessageVerifiers in Rails, including the one protecting cookies.
secret_key_base: 492f...
Изменение secret_key_base при использовании CookieStore
делает все предыдущие сессии невалидными.
В контроллере можно получить доступ к сессии с помощью метода экземпляра session
.
Сессии лениво загружаются. Если не получать доступ к сессиям в коде экшна, они не будут загружаться. Следовательно, никогда не придется отключать сессии, просто не обращайтесь к ним, и они будут выполнять свое задание.
Значения сессии хранятся, используя пары ключ/значение, подобно хэшу:
class ApplicationController < ActionController::Base
private
# Находим пользователя с ID, хранящимся в сессии с ключом
# :current_user_id Это обычный способ обрабатывать вход пользователя
# в приложении на Rails; вход устанавливает значение сессии, а
# выход убирает его.
def current_user
@_current_user ||= session[:current_user_id] &&
User.find_by(id: session[:current_user_id])
end
end
Чтобы что-то хранить в сессии, просто присвойте это ключу, как в хэше:
class LoginsController < ApplicationController
# "Создаем" логин (при входе пользователя)
def create
if user = User.authenticate(params[:username], params[:password])
# Сохраняем ID пользователя в сессии, так что он может быть использован
# в последующих запросах
session[:current_user_id] = user.id
redirect_to root_url, status: :see_other root_url
end
end
end
Чтобы убрать что-то из сессии, удалите пару ключ/значение:
class LoginsController < ApplicationController
# "Удаляем" логин (при выходе пользователя)
def destroy
# Убираем id пользователя из сессии
session.delete(:current_user_id)
# Очистить мемоизированного текущего пользователя
@_current_user = nil
redirect_to root_url, status: :see_other
end
end
Для сброса текущей сессии, используйте reset_session
.
Flash - это специальная часть сессии, которая очищается с каждым запросом. Это означает, что сохраненные там значения будут доступны только в следующем запросе, что полезно для передачи сообщений об ошибках и т.п.
К флэшу можно получить доступ с помощью метода flash
. Подобно сессии, флэш представлен хэшем.
Давайте рассмотрим случай логаута в качестве примера. Контроллер может послать сообщение, которое будет отображено пользователю при следующем запросе:
class LoginsController < ApplicationController
def destroy
session.delete(:current_user_id)
flash[:notice] = "You have successfully logged out."
redirect_to root_url
end
end
Отметьте, что также возможно назначить сообщение флэш как часть перенаправления. Можно назначить :notice
, :alert
или общего назначения :flash
:
redirect_to root_url, notice: "You have successfully logged out."
redirect_to root_url, alert: "You're stuck here!"
redirect_to root_url, flash: { referral_code: 1234 }
Экшн destroy
перенаправляет на root_url
приложения, где будет отображено сообщение. Отметьте, что от следующего экшна полностью зависит решение, будет ли он или не будет что-то делать с тем, что предыдущий экшн вложил во flash. Принято отображать любые сообщения об ошибке или уведомления из flash в макете приложения:
<html>
<!-- <head/> -->
<body>
<% flash.each do |name, msg| -%>
<%= content_tag :div, msg, class: name %>
<% end -%>
<!-- дальнейшее содержимое -->
</body>
</html>
В этом случае, если экшн установил сообщения уведомления или предупреждения, макет отобразит их автоматически.
Можно передать все, что только сессия может хранить; вы не ограничены уведомлениями или предупреждениями:
<% if flash[:just_signed_up] %>
<p class="welcome">Welcome to our site!</p>
<% end %>
Если хотите, чтобы значение flash было перенесено в другой запрос, используйте flash.keep
:
class MainController < ApplicationController
# Давайте скажем этому экшну, соответствующему root_url, что хотим
# все запросы сюда перенаправить на UsersController#index. Если
# экшн установил flash и направил сюда, значения в нормальной ситуации
# будут потеряны, когда произойдет другой редирект, но Вы можете
# использовать 'keep', чтобы сделать его персистентным для другого запроса.
def index
# Все значения flash будут персистентными.
flash.keep
# Можете также использовать ключ для сохранения определенных значений.
# flash.keep(:notice)
redirect_to users_url
end
end
flash.now
По умолчанию, добавление значений во flash делает их доступными для следующего запроса, но иногда хочется иметь доступ к этим значениям в том же запросе. Например, если экшн create
проваливается при сохранении ресурса, и будет отрендерен непосредственно макет new
, что не приведет к новому запросу, но все равно можно отобразить сообщение, используя flash. Чтобы это сделать, используйте flash.now
так же, как используете обычный flash
:
class ClientsController < ApplicationController
def create
@client = Client.new(client_params)
if @client.save
# ...
else
flash.now[:error] = "Could not save client"
render action: "new"
end
end
end
Приложение может хранить небольшое количество данных у клиента - в так называемых куки - которые будут персистентными между запросами и даже сессиями. Rails обеспечивает простой доступ к куки посредством метода cookies
, который - очень похож на session
- и работает как хэш:
class CommentsController < ApplicationController
def new
# Автозаполнение имени комментатора, если оно хранится в куки.
@comment = Comment.new(author: cookies[:commenter_name])
end
def create
@comment = Comment.new(comment_params)
if @comment.save
flash[:notice] = "Thanks for your comment!"
if params[:remember_name]
# Запоминаем имя комментатора.
cookies[:commenter_name] = @comment.author
else
# Удаляем из куки имя комментатора, если оно есть.
cookies.delete(:commenter_name)
end
redirect_to @comment.article
else
render action: "new"
end
end
end
Отметьте, что если для удаления значений сессии можно установить ключ в nil
, то для удаления значений куки следует использовать cookies.delete(:key)
.
Rails также предоставляет подписанные куки и зашифрованные куки для хранения чувствительных данных. В подписанные куки добавляется криптографическая сигнатура значений куки для защиты их целостности. Зашифрованные куки шифруют значения в дополнение к их подписи, поэтому они не могут быть прочитаны пользователем. За подробностями обратитесь к документации API.
Эти специальные куки используют сериализатор для сериализации назначенных значений в строки и десериализации их в объекты Ruby при чтении. Можно определить, какой сериализатор использовать с помощью config.action_dispatch.cookies_serializer
.
Значением по умолчанию для сериализатора в новых приложениях :json
. Следует быть осторожным, потому что JSON обладает ограниченной поддержкой обратной сериализации объектов Ruby. Например, объекты Date
, Time
и Symbol
(включая ключи Hash
) будут сериализованы и десериализованы в String
:
class CookiesController < ApplicationController
def set_cookie
cookies.encrypted[:expiration_date] = Date.tomorrow # => Thu, 20 Mar 2014
redirect_to action: 'read_cookie'
end
def read_cookie
cookies.encrypted[:expiration_date] # => "2014-03-20"
end
end
Если вам нужно сохранить эти или более сложные объекты, вам может потребоваться вручную преобразовать их значения при чтении их в последующих запросах.
Если вы храните сессию в куки, все вышесказанное также применяется к хэшам session
и flash
.
ActionController упрощает рендеринг данных в форматах HTML, XML или JSON. Если сгенерируете контроллер с помощью скаффолда, то он будет выглядеть следующим образом.
class UsersController < ApplicationController
def index
@users = User.all
respond_to do |format|
format.html # index.html.erb
format.xml { render xml: @users }
format.json { render json: @users }
end
end
end
Отметьте, что в вышеописанном коде использован render xml: @users
, а не render xml: @users.to_xml
. Если объект не String, то Rails автоматически вызовет to_xml
.
Подробнее о рендеринге можно узнать в Руководстве Макеты и рендеринг в Rails.
Колбэки экшна - это методы, которые запускаются "до", "после" или "до и после" экшна контроллера.
Колбэки экшна наследуются, поэтому, если вы установите один в ApplicationController
, он будет запущен в каждом контроллере вашего приложения.
Колбэки экшна "before" регистрируются с помощью before_action
. Они могут прерывать цикл запроса. Обычный колбэк экшна "before" - это, например, тот, который требует, чтобы пользователь был авторизован для запуска экшна. Метод можно определить следующим образом:
class ApplicationController < ActionController::Base
before_action :require_login
private
def require_login
unless logged_in?
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url # halts request cycle
end
end
end
Метод просто записывает сообщение об ошибке во flash и перенаправляет на форму авторизации, если пользователь не авторизовался. Если колбэк экшна "before" рендерит или перенаправляет, экшн контроллера не запустится. Если есть дополнительные колбэки экшна в очереди, они также будут отменены.
В этом примере колбэк экшна добавлен в ApplicationController
, и поэтому все контроллеры в приложении наследуют его. Это приводит к тому, что всё в приложении требует, чтобы пользователь был авторизован, чтобы пользоваться им. По понятным причинам (пользователь не сможет зарегистрироваться в первую очередь!), не все контроллеры или экшны должны требовать его. Вы можете не допустить запуск этого фильтра перед определенными экшнами с помощью skip_before_action
:
class LoginsController < ApplicationController
skip_before_action :require_login, only: [:new, :create]
end
Теперь, экшны LoginsController
new
и create
будут работать как раньше, без требования к пользователю быть зарегистрированным. Опция :only
используется для пропуска колбэка экшна только для этих экшнов, а также есть опция :except
, которая работает наоборот. Эти опции можно использовать и при добавлении колбэков экшна, поэтому необходимо добавить колбэк, который запускается только для выбранных экшнов в первую очередь.
Вызов одного и того же колбэка экшна несколько раз с разными опциями не будет работать, поскольку последнее определение колбэка экшна перезапишет предыдущие.
В дополнение к колбэкам экшна "before", можно запустить колбэки экшна после того, как экшн контроллера был выполнен, или "и до, и после".
Колбэки экшна "after" регистрируются с помощью after_action
. Они похожи на колбэки экшна "before", но поскольку экшн контроллера уже был запущен, у них есть доступ к данным отклика, которые будут отосланы клиенту. Очевидно, колбэки экшна "after" не могут остановить экшн от запуска. Обратите внимание, что колбэки экшна "after" выполняются только после успешного выполнения экшна контроллера, но не при возникновении исключения в цикле запроса.
Колбэки экшна "around" регистрируются с помощью around_action
. Они ответственны за запуск связанных с ними экшнов с помощью yield, подобно тому, как работают промежуточные программы Rack.
Например, на веб-сайте, где для изменений есть процедура утверждения информации, администратор может легко их просмотреть, применив их внутри транзакции:
class ChangesController < ApplicationController
around_action :wrap_in_transaction, only: :show
private
def wrap_in_transaction
ActiveRecord::Base.transaction do
begin
yield
ensure
raise ActiveRecord::Rollback
end
end
end
end
Отметьте, что колбэки экшна "around" также оборачивают рендеринг. В частности, в вышеуказанном примере, если вью сама начнет считывать из базы данных (например через скоуп), она это осуществит внутри транзакции, предоставив, таким образом, данные для предварительного просмотра.
Можно не вызывать yield и создать отклик самостоятельно, в этом случае экшн контроллера не будет запущен.
Хотя наиболее распространенный способ использование колбэков экшна - это создание private методов и использование before_action
, after_action
или around_action
для их добавления, есть два других способа делать то же самое.
Первый - это использовать блок прямо в методах *_action
. Блок получает контроллер как аргумент. Колбэк экшна require_login
может быть переписан с использованием блока:
class ApplicationController < ActionController::Base
before_action do |controller|
unless controller.send(:logged_in?)
flash[:error] = "You must be logged in to access this section"
redirect_to new_login_url
end
end
end
Отметьте, что колбэк экшна в этом случае использует метод send
, так как logged_in?
является private, и колбэк экшна не запустится в области видимости контроллера. Это не рекомендуемый способ применения такого особого колбэка экшна, но в простых задачах он может быть полезен.
В частности, для around_action
в блок также вкладывается action
:
around_action { |_controller, action| time(&action) }
Второй способ - это использовать класс (фактически, подойдет любой объект, реагирующий правильными методами) для управления колбэком экшна. Это полезно для более сложных задач, которые не могут быть осуществлены предыдущими двумя способами по причине трудности читаемости и повторного использования. Как пример, можете переписать фильтр авторизации снова, использовав класс:
class ApplicationController < ActionController::Base
before_action LoginActionCallback
end
class LoginActionCallback
def self.before(controller)
unless controller.send(:logged_in?)
controller.flash[:error] = "You must be logged in to access this section"
controller.redirect_to controller.new_login_url
end
end
end
Опять же, это не идеальный пример для этого колбэка экшна, поскольку он не запускается в области видимости контроллера, а получает контроллер как аргумент. Класс должен реализовывать метод с тем же именем, что и колбэк экшна, поэтому для колбэка экшна before_action
класс должен реализовать метод before
, и так далее. Метод around
должен иметь yield
для выполнения экшна.
Межсайтовая подделка запроса (CSRF, Cross-Site Request Forgery) - это тип атаки, в которой сайт обманом заставляет пользователя сделать запрос на другой сайт, возможно, добавляя, модифицируя или удаляя данные на этом сайте без ведома или прав доступа пользователя.
Первый шаг, чтобы избежать это - убедиться, что все "разрушительные" экшны (создание, обновление и уничтожение) могут быть доступны только не-GET-запросам. Если вы следуете соглашениям RESTful, то уже делаете это. Однако, сайт злоумышленника может также легко послать не-GET-запрос на ваш сайт, поэтому и необходима защита от подделки запроса. Как сказано в названии, он защищает от подделки запроса.
Это можно сделать, добавив неугадываемый токен, известный только вашему серверу, в каждый запрос. При этом способе, если запрос приходит без подходящего токена, ему будет отказано в доступе.
Если вы генерируете подобную форму:
<%= form_with model: @user do |form| %>
<%= form.text_field :username %>
<%= form.text_field :password %>
<% end %>
то увидите, как токен будет добавлен в скрытое поле:
<form accept-charset="UTF-8" action="/users/1" method="post">
<input type="hidden"
value="67250ab105eb5ad10851c00a5621854a23af5489"
name="authenticity_token"/>
<!-- fields -->
</form>
Rails добавит этот токен в каждую форму, генерируемую с помощью хелперов форм, таким образом, большую часть времени можете об этом не беспокоиться. Если вы пишете формы вручную или хотите добавить токен по другой причине, это можно сделать, используя метод form_authenticity_token
:
form_authenticity_token
генерирует валидный аутентификационный токен. Его полезно размещать в тех местах, куда Rails не добавляет его автоматически, например в произвольные вызовы Ajax.
В руководстве Безопасность приложений на Rails имеется более подробная информация об этом, и множество других вопросов, посвященных безопасности, которые вы должны принимать во внимание при разработке веб-приложения.
В каждом контроллере есть два акцессор-метода, указывающих на объекты запроса и отклика, связанные с циклом запроса, находящегося в текущее время на стадии выполнения. Метод request
содержит экземпляр ActionDispatch::Request
, а метод response
возвращает объект отклика, представляющий то, что будет отправлено обратно на клиента.
request
Объект request содержит множество полезной информации о запросе, полученном с клиента. Чтобы получить полный перечень доступных методов, обратитесь к документации по Rails API и документации по Rack. В числе свойств, доступных для этого объекта, следующие:
Свойство request |
Назначение |
---|---|
host |
Имя хоста, используемого для этого запроса. |
domain(n=2) |
Первые n сегментов имени хоста, начиная справа (домен верхнего уровня). |
format |
Тип содержимого, запрошенного с клиента. |
method |
Метод HTTP, использованного для запроса. |
get? , post? , patch? , put? , delete? , head? |
Возвращает true, если метод HTTP - это GET/POST/PATCH/PUT/DELETE/HEAD. |
headers |
Возвращает хэш, содержащий заголовки, связанные с запросом. |
port |
Номер порта (целое число), использованного для запроса. |
protocol |
Возвращает строку, содержащую использованный протокол плюс "://", например "http://". |
query_string |
Часть URL со строкой запроса, т.е. все после "?". |
remote_ip |
Адрес IP клиента. |
url |
Полный URL, использованный для запроса. |
path_parameters
, query_parameters
и request_parameters
Rails собирает все параметры, посланные вместе с запросом, в хэше params
, были ли они посланы как часть строки запроса, либо в теле запроса post. У объекта request имеется три акцессора, которые предоставляют доступ к этим параметрам в зависимости от того, откуда они пришли. Хэш query_parameters
содержит параметры, посланные как часть строки запроса, в то время как хэш request_parameters
содержит параметры, посланные как часть тела post. Хэш path_parameters
содержит параметры, распознанные роутингом как часть пути, ведущего к определенному контроллеру и экшну.
response
Объект response (отклик) обычно не используется напрямую, а создается во время выполнения экшна и рендеринга данных, которые посылаются обратно пользователю, но иногда - например, в последующем колбэке экшна - бывает полезно иметь доступ к отклику напрямую. Некоторые из этих акцессор-методов имеют сеттеры, позволяющие изменять их значения. Чтобы получить полный перечень доступных методов, обратитесь к документации по Rails API и документации по Rack.
Свойство response |
Назначение |
---|---|
body |
Это строка данных, которая будет возвращена клиенту. Чаще всего это HTML. |
status |
Код статуса HTTP для отклика, например 200 для успешного запроса или 404 для ненайденного файла. |
location |
URL, по которому клиент будет перенаправлен, если указан. |
content_type |
Тип содержимого отклика. |
charset |
Кодировка, используемая для отклика. По умолчанию это "utf-8". |
headers |
Заголовки, используемые для отклика. |
Если хотите установить произвольные заголовки для отклика, то response.headers
- как раз то место, что нужно. Атрибут headers - это хэш, который связывает имена заголовков с их значениями, а Rails устанавливает некоторые из них автоматически. Если нужно добавить или изменить заголовок, просто назначьте его response.headers
следующим образом:
response.headers["Content-Type"] = "application/pdf"
В вышеприведенном случае более очевидным было бы использование сеттера content_type
.
Rails поставляется с тремя встроенными механизмами аутентификации HTTP:
Базовая аутентификация HTTP - это аутентификационная схема, поддерживаемая большинством браузеров и других клиентов HTTP. Как пример, рассмотрим раздел администрирования, который доступен только при вводе имени пользователя и пароля в основном диалоговом окне браузера. Использование встроенной аутентификации требует использования одного метода http_basic_authenticate_with
.
class AdminsController < ApplicationController
http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end
Благодаря этому можно создавать именованные контроллеры, наследуемые от AdminsController
. Таким образом, предварительный колбэк экшна будет запущен для всех экшнов в этих контроллерах, защищая их с помощью базовой аутентификации HTTP
.
Дайджест-аутентификация HTTP превосходит базовую аутентификацию, так как она не требует от клиента посылать незашифрованный пароль по сети (хотя базовая аутентификация HTTP безопасна через HTTPS). Чтобы использовать дайджест-аутентификацию с Rails требуется только один метод authenticate_or_request_with_http_digest
.
class AdminsController < ApplicationController
USERS = { "lifo" => "world" }
before_action :authenticate
private
def authenticate
authenticate_or_request_with_http_digest do |username|
USERS[username]
end
end
end
Как мы видим из примера, блок authenticate_or_request_with_http_digest
принимает только один аргумент - имя пользователя. И блок возвращает пароль. Возврат false
или nil
из authenticate_or_request_with_http_digest
вызовет провал аутентификации.
Аутентификация HTTP по токену — это схема для использования токенов Bearer в заголовке HTTP Authorization
. Имеется множество доступных форматов токенов, но их описание выходит за рамки этой документации.
В качестве примера, предположим вы хотите использовать аутентификационный токен, выпущенный заранее, для аутентификации и доступа. Реализация аутентификации по токену с помощью Rails требует использования лишь одного метода, authenticate_or_request_with_http_token
.
class PostsController < ApplicationController
TOKEN = "secret"
before_action :authenticate
private
def authenticate
authenticate_or_request_with_http_token do |token, options|
ActiveSupport::SecurityUtils.secure_compare(token, TOKEN)
end
end
end
Как видно из примера, блок authenticate_or_request_with_http_token
принимает два аргумента - токен и Hash
, содержащий опции, которые были взяты из заголовка HTTP Authorization
. Блок должен вернуть true
, если аутентификация успешная. Возврат false
или nil
в нем вызовет неудачу в аутентификации.
Иногда хочется послать пользователю файл вместо рендеринга страницы HTML. Все контроллеры в Rails имеют методы send_data
и send_file
, которые направляют данные на клиента. send_file
- это удобный метод, который позволяет указать имя файла на диске, а он направит содержимое этого файла вам.
Чтобы направить данные на клиента, используйте send_data
:
require "prawn"
class ClientsController < ApplicationController
# Генерирует документ PDF с информацией на клиента и возвращает
# его. Пользователь получает PDF как загрузку файла.
def download_pdf
client = Client.find(params[:id])
send_data generate_pdf(client),
filename: "#{client.name}.pdf",
type: "application/pdf"
end
private
def generate_pdf(client)
Prawn::Document.new do
text client.name, align: :center
text "Address: #{client.address}"
text "Email: #{client.email}"
end.render
end
end
Экшн download_pdf
в примере вызовет private метод, который фактически сгенерирует документ PDF и возвратит его как строку. Эта строка будет направлена клиенту как загрузка файла, и пользователю будет предложено имя файла. Иногда при потоковой передаче файлов пользователю может не потребоваться загрузка файла. Возьмите, например, изображения, которые могут быть встроены в страницы HTML. Чтобы сказать браузеру, что файл не предназначен для скачивания, нужно установить опцию :disposition
как "inline". Противоположное дефолтное значение этой опции - "attachment".
Если хотите отправить файл, уже существующий на диске, используйте метод send_file
.
class ClientsController < ApplicationController
# Потоковая передача файла, который уже был сгенерирован и сохранен на диск.
def download_pdf
client = Client.find(params[:id])
send_file("#{Rails.root}/files/clients/#{client.id}.pdf",
filename: "#{client.name}.pdf",
type: "application/pdf")
end
end
Это прочтет и передаст файл блоками в 4 Кбайт за раз, избегая загрузки в память сразу целого файла. Можно отключить потоковую передачу с помощью опции :stream
или отрегулировать размер блока с помощью опции :buffer_size
.
Если не указан :type
, он будет определяться по расширению файла, указанного в :filename
. Если для расширения не зарегистрирован тип содержимого, будет использован application/octet-stream
.
Будьте осторожны, когда используете данные, пришедшие с клиента (params, куки и т.д.), для обнаружения файла на диске, так как есть риск безопасности в том, что кто-то может получить доступ к файлам, к которым иметь он не должен.
Не рекомендуется передавать в потоке статичные файлы через Rails, если можно вместо этого разместить их в папке public на веб-сервере. Более эффективно разрешить пользователям скачивать файлы напрямую, используя Apache или другой веб-сервер, сохраняя запрос от ненужного прогона через весь стек Rails.
Хотя send_data
работает прекрасно, если вы создаете приложение на принципах RESTful, наличие отдельных экшнов для загрузок файла обычно не требуется. В терминологии REST файл PDF из вышеприведенного примера можно рассматривать еще одним представлением ресурса client. Rails предоставляет элегантный способ реализации "RESTful" загрузок. Вот как можно переписать пример, чтобы загрузка PDF была частью экшна show
без какой-либо потоковой передачи:
class ClientsController < ApplicationController
# Пользователь может запросить получение этого ресурса как HTML или PDF.
def show
@client = Client.find(params[:id])
respond_to do |format|
format.html
format.pdf { render pdf: generate_pdf(@client) }
end
end
end
Чтобы этот пример заработал, нужно добавить PDF тип MIME в Rails. Это можно сделать, добавив следующую строчку в файл config/initializers/mime_types.rb
:
Mime::Type.register "application/pdf", :pdf
Конфигурационные файлы не перезагружаются с каждым запросом, поэтому необходимо перезапустить сервер для того, чтобы их изменения вступили в силу.
Теперь пользователь может запрашивать получение версии в PDF, просто добавив ".pdf" в URL:
GET /clients/1.pdf
Rails позволяет отдавать в потоке не только файлы. Фактически, в объекте отклика можно отдать все, что хотите. Модуль ActionController::Live
позволяет создать персистентное соединение с браузером. Используя этот модуль, можно послать в браузер произвольные данные в определенные моменты времени.
Включение ActionController::Live
в класс вашего контроллера предоставит всем экшнам контроллера возможность отдавать данные в потоке. Этот модуль можно включить следующим образом:
class MyController < ActionController::Base
include ActionController::Live
def stream
response.headers['Content-Type'] = 'text/event-stream'
100.times {
response.stream.write "hello world\n"
sleep 1
}
ensure
response.stream.close
end
end
Вышеприведенный код будет поддерживать персистентное соединение с браузером и пошлет 100 сообщений "hello world\n"
, раз в секунду каждое.
В вышеприведенном примере нужно обратить внимание на ряд вещей. Необходимо убедиться, что потоковый отклик будет закрыт. Если забыть закрыть, поток оставит навсегда открытым сокет. Также необходимо установить тип содержимого text/event-stream
до записи в поток отклика. Это так, потому что заголовки не могут быть записаны после того, как отклик был совершен (когда response.committed?
возвращает истинное значение), которое возникает, когда вызывается write
или commit
для потокового отклика.
Предположим, мы создаем машину караоке, и пользователь хочет получить слова для определенной песни. В каждом Song
имеется определенное количество строчек, и у каждой строчки есть время num_beats
для завершения пения.
Если мы хотим возвращать слова по принципу караоке (посылая строчку, только когда певец закончил предыдущую), можно использовать ActionController::Live
следующим образом:
class LyricsController < ActionController::Base
include ActionController::Live
def show
response.headers['Content-Type'] = 'text/event-stream'
song = Song.find(params[:id])
song.each do |line|
response.stream.write line.lyrics
sleep line.num_beats
end
ensure
response.stream.close
end
end
Вышеприведенный код посылает следующую строчку только после того, как певец завершил предыдущую строчку.
Потоковая передача произвольных данных – чрезвычайно мощный инструмент. Как показано в предыдущих примерах, можно выбирать, когда и что посылать в потоковом отклике. Однако, также необходимо отметить следующие вещи:
close
при использовании потокового отклика.
ActionController::Live
не будет работать. Необходимо использовать веб-сервер, не буферизирующий отклики автоматически.
Rails ведет лог-файл для каждой среды в папке log
. Это чрезвычайно полезно при отладке того, что происходит в приложении, но в реальной жизни может быть не нужно хранение каждого бита информации в лог-файле.
Можно фильтровать чувствительные параметры запроса в файлах лога, присоединив их к config.filter_parameters
в настройках приложения. Эти параметры будут помечены в логе как [FILTERED].
config.filter_parameters << :password
Предоставленные параметры будут отфильтрованы с помощью частично соответствующего регулярного выражения. Rails добавляет список фильтров по умолчанию, включающий :passw
, :secret
и :token
, в соответствующем инициализаторе (initializers/filter_parameter_logging.rb
), чтобы обрабатывать типичные параметры приложения, такие как password
, password_confirmation
и my_token
.
Иногда нужно фильтровать из файлов лога некоторые чувствительные места расположения, на которые перенаправляет приложение. Это можно осуществить с использованием конфигурационной опции config.filter_redirect
:
config.filter_redirect << 's3.amazonaws.com'
Ей можно передать строку, регулярное выражение или массив из них.
config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]
Соответствующие URL будут помечены как '[FILTERED]'. Впрочем, если вы хотите фильтровать только параметры, а не целые URL-адреса, ознакомьтесь с разделом Фильтрация параметров.
Скорее всего, ваше приложение будет содержать программные ошибки или, другими словами, вызывать исключения, которые нужно обработать. Например, если пользователь переходит по ссылке на ресурс, который больше не существует в базе данных, Active Record вызовет исключение ActiveRecord::RecordNotFound
.
Дефолтный обработчик исключений Rails отображает сообщение "500 Server Error" для всех исключений. Если запрос сделан локально, отображается прекрасная трассировка и добавляется дополнительная информация, чтобы можно было выяснить, что пошло не так, и разобраться с этим. Если запрос был удаленным, Rails отобразит пользователю лишь простое сообщение "500 Server Error", или "404 Not Found", если была проблема с роутингом или запись не была найдена. Иногда может понадобиться настроить, как эти ошибки будут перехвачены и как они будут отображены пользователю. В приложении на Rails доступны несколько уровней обработки исключений:
По умолчанию в среде production приложение будет рендерить или 404, или 500 сообщение об ошибке. В среде development будут просто вызываться все необрабатываемые исключения. Эти сообщения содержатся в статичных файлах HTML в папке public, в 404.html
и 500.html
соответственно. Можно настроить эти файлы, добавив дополнительную информацию и стили, но помните, что они статичные; т.е. нельзя использовать ERB, SCSS, CoffeeScript или макеты для них.
rescue_from
Если хотите сделать нечто более сложное при перехвате ошибок, можете использовать rescue_from
, который обрабатывает исключения определенного типа (или нескольких типов) во всем контроллере и его подклассах.
Когда возникает исключение, которое перехватывается директивой rescue_from
, объект исключения передается в обработчик. Обработчик может быть методом или объектом Proc
, переданным опции :with
. Также можно использовать блок вместо объекта Proc
.
Вот как можно использовать rescue_from
для перехвата всех ошибок ActiveRecord::RecordNotFound
и что-то с ними делать.
class ApplicationController < ActionController::Base
rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
private
def record_not_found
render plain: "404 Not Found", status: 404
end
end
Конечно, этот пример далеко недоработан, и ничуть не улучшает обработку исключений по умолчанию, но раз вы уже перехватили все эти исключения, то вольны делать с ними все, что хотите. Например, можете создать свои классы исключений, которые будут вызваны, когда у пользователя нет доступа в определенные разделы вашего приложения:
class ApplicationController < ActionController::Base
rescue_from User::NotAuthorized, with: :user_not_authorized
private
def user_not_authorized
flash[:error] = "You don't have access to this section."
redirect_back(fallback_location: root_path)
end
end
class ClientsController < ApplicationController
# Проверим, что пользователь имеет права авторизации для доступа к клиентам.
before_action :check_authorization
# Отметьте, как экшны не беспокоятся об авторизационных делах.
def edit
@client = Client.find(params[:id])
end
private
# Если пользователь не авторизован, просто вызываем исключение.
def check_authorization
raise User::NotAuthorized unless current_user.admin?
end
end
Использование rescue_from
c Exception
или StandardError
вызовет серьезные побочные эффекты, поскольку это препятствует Rails правильно обрабатывать исключения. Таким образом, это не рекомендуется делать, если нет для того веской причины.
При запуске в среде running production все ошибки ActiveRecord::RecordNotFound
рендерят страницу ошибки 404. Если вам не нужно другое поведение, их не нужно обрабатывать.
Некоторые исключения перехватываемы только из класса ApplicationController
, так как они вызываются до того, как контроллер будет инициализирован и экшны будут выполнены.
Если необходимо обеспечить доступ к определенному контроллеру только через HTTPS, нужно сделать это, включив промежуточную программу ActionDispatch::SSL
через config.force_ssl
в конфигурациях среды.
Rails также поставляется со встроенной конечной точкой проверки работоспособности, доступной по пути /up
. Эта конечная точка возвращает код состояния 200, если приложение успешно загрузилось без исключений, и код состояния 500 в противном случае.
В production многим приложениям требуется сообщать свой статус вышестоящим системам, будь то монитор работоспособности, который уведомляет инженеров при возникновении сбоев, или балансировщик нагрузки или контроллер Kubernetes, используемый для определения работоспособности пода. Эта проверка работоспособности является универсальной и подходит для большинства ситуаций.
Хотя все вновь созданные приложения Rails будут иметь проверку работоспособности по пути /up
, вы можете настроить этот путь по своему желанию в файле "config/routes.rb":
Rails.application.routes.draw do
get "healthz" => "rails/health#show", as: :rails_health_check
end
Теперь проверка работоспособности будет доступна по пути /healthz
.
эта конечная точка не отражает состояние всех зависимостей вашего приложения, таких как база данных или кластер Redis. Замените "rails/health#show" на свой собственный экшн контроллера, если у вас есть специфические для приложения потребности.
Тщательно продумывайте, что вы хотите проверять, поскольку это может привести к ситуациям, когда ваше приложение перезапускается из-за сбоя стороннего сервиса. В идеале ваше приложение должно быть спроектировано так, чтобы элегантно обрабатывать такие сбои.