Обзор Action Controller
По этому руководству вы изучите, как работают контроллеры, и как они вписываются в цикл запроса к вашему приложению.
После его прочтения, вы узнаете:
- Как следить за ходом запроса через контроллер.
- Как ограничить параметры, переданные в контроллер.
- Как и зачем хранятся данные в сессии или куки.
- Как работать с фильтрами для исполнения кода в течение обработки запроса.
- Как использовать встроенную в Action Controller HTTP аутентификацию.
- Как направлять потоковые данные прямо в браузер пользователя.
- Как отфильтровывать деликатные параметры, чтобы они не появлялись в логах приложения.
- Как работать с исключениями, которые могут порождаться в течение обработки запроса.
Что делает контроллер?
Action Controller это C в аббревиатуре MVC. После того, как роутер определит, какой контроллер использовать для обработки запроса, контроллер ответственен за осмысление запроса и генерацию подходящего ответа. К счастью, Action Controller делает за вас большую часть грязной работы и использует элегантные соглашения, чтобы сделать это по возможности максимально просто.
Для большинства приложений, основанных на RESTful, контроллер получает запрос (это невидимо для вас, как для разработчика), извлекает или сохраняет данные в модели и использует вьюху для создания результирующего HTML. Если контроллеру необходимо работать немного по другому, не проблема, это всего лишь наиболее распространенный способ работы контроллера.
Таким образом, контроллер можно рассматривать как посредника между моделями и вьюхами. Он делает данные модели доступными вьюхе, так что она может отображать эти данные пользователю, и он сохраняет или обновляет данные от пользователя в модель.
NOTE: Более детально о процессе маршрутизации смотрите Роутинг в Rails.
Соглашение по именованию контроллеров
Соглашение по именованию контроллеров в Rails устанавливает предпочтение множественного числа в последнем слове имени контроллера, хотя строго это не требуется (например, ApplicationController
). К примеру, ClientsController
более предпочтителен, чем ClientController
, SiteAdminsController
более предпочтителен, чем SiteAdminController
или SitesAdminsController
, и так далее.
Следование этому соглашению позволяет вам использовать генераторы маршрутов по умолчанию (например, resources
и т.п.) без необходимости определять каждый :path
или :controller
, и сохраняет последовательным использование хелперов URL и путей во всем вашем приложении. Подробнее смотрите в Руководстве по макетам и рендерингу.
NOTE: Соглашение по именованию контроллеров отличается от соглашения по именованию моделей, которые, как ожидается, будут именоваться в единственном числе.
Методы и экшны
Контроллер - это класс Ruby, унаследованный от ApplicationController
и содержащий методы, как и любой другой класс. Когда ваше приложение получает запрос, роутинг определяет, какой контроллер и экшн нужно запустить, затем Rails создает экземпляр этого контроллера и запускает метод с именем, как у экшна.
class ClientsController < ApplicationController
def new
end
end
В качестве примера, если пользователь перейдет в /clients/new
в вашем приложении, чтобы добавить нового клиента, Rails создаст экземпляр ClientsController
и вызовет метод new
. Отметьте, что пустой метод из вышеприведенного примера будет прекрасно работать, так как Rails по умолчанию отрендерит вьюху new.html.erb
, если в экшне не будет указано иное. Метод new
может сделать доступной для вьюхи переменную экземпляра @client
для создания нового Client
:
def new
@client = Client.new
end
Руководство Макеты и рендеринг в Rails объясняет это более детально.
ApplicationController
унаследован от ActionController::Base
, который определяет несколько полезных методов. Это руководство раскроет часть из них, но если вы любопытны, можете увидеть их все в документации по API.
Только public методы могут быть вызваны как экшны. Хорошей практикой является уменьшение области видимости методов (при помощи private
или protected
), не предназначенных быть экшнами, таких как вспомогательные методы и фильтры.
Параметры
Возможно, вы хотите получить доступ к данным, посланным пользователем, или к другим параметрам в экшнах вашего контроллера. Существует два типа параметров, доступных в веб-приложениях. Первый - это параметры, посланные как часть 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
NOTE: Фактический URL в этом примере будет перекодирован как "/clients?ids%5b%5d=1&ids%5b%5d=2&ids%5b%5b=3", так как "[" и "]" не допустимы в URL. В основном, вам не придется беспокоиться об этом, так как браузер позаботится об этом за вас, а Rails декодирует это обратно, когда получит, но если вы когда-нибудь будете отправлять эти запросы вручную, имейте это в виду.
Значение params[:ids]
теперь будет ["1", "2", "3"]
. Отметьте, что значения параметра всегда строковое; Rails не делает попыток угадать или предсказать тип.
NOTE: Значения, такие как [nil]
или [nil, nil, ...]
в params
по умолчанию заменяются на []
по причине безопасности. Подробнее смотрите в Руководстве по безопасности.
Чтобы послать хэш, следует заключить имя ключа в скобки:
<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
ведет себя как хэш, но позволяет взаимозаменяемо использовать символы и строки как ключи.
Параметры JSON
Если вы пишете приложение веб-сервиса, возможно вам более комфортно принимать параметры в формате 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.
NOTE: Поддержка парсинга параметров XML была извлечена в гем actionpack-xml_parser
.
Параметры роутинга
Хэш params
будет всегда содержать ключи :controller
и :action
, но следует использовать методы controller_name
и action_name
вместо них для доступа к этим значениям. Любой другой параметр, определенный роутингом, такой как :id
, также будет доступен. Например, рассмотрим перечень клиентов, где список может быть показан либо для активных, либо для неактивных клиентов. Мы можем добавить маршрут, который перехватывает параметр :status
в "красивом" URL:
get '/clients/:status' => 'clients#index', foo: 'bar'
В этом случае, когда пользователь откроет URL /clients/active
, params[:status]
будет установлен в "active". Когда использован этот маршрут, params[:foo]
также будет установлен в "bar", как будто он был передан в строке запроса. Ваш контроллер также получит params[:action]
как "index" и params[:controller]
как "clients".
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
С помощью сильных параметров (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
Разрешенные скалярные значения
Для данного
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])
За пределами области видимости Strong Parameters
Strong parameter API был разработан для наиболее общих вариантов использования. Это не панацея от всех ваших проблем белого списка. Однако можно легко смешивать API с вашим собственным кодом для адаптации к вашей ситуации.
Рассмотрим ситуацию, когда есть параметры, представляющие имя продукта, и хэш произвольных данных, связанных с этим продуктом, и нужно поместить в белый список атрибут name продукта, а также весь хэш данных. API в strong parameters не позволит явно добавить в белый список весь вложенный хэш с любыми ключами, но можно использовать ключи вложенного хэша для объявления, что следует добавить в белый список:
def product_params
params.require(:product).permit(:name, data: params[:product][:data].try(:keys))
end
Сессия
В вашем приложении есть сессия для каждого пользователя, в которой можно хранить небольшие объемы данных, которые будут сохраняться между запросами. Сессия доступна только в контроллере и во вьюхе, и может использовать один из нескольких механизмов хранения:
ActionDispatch::Session::CookieStore
- Хранит все на клиенте.ActionDispatch::Session::CacheStore
- Хранит данные в кэше Rails.ActionDispatch::Session::ActiveRecordStore
- Хранит данные в базе данных с использованием Active Record. (требует гемactiverecord-session_store
).ActionDispatch::Session::MemCacheStore
- Хранит данные в кластере memcached (эта устаревшая реализация, вместо нее рассмотрите использование CacheStore).
Все хранилища сессии используют куки для хранения уникального ID каждой сессии (вы должны использовать куки, Rails не позволяет передавать ID сессии в URL, так как это менее безопасно).
В большинстве хранилищ этот ID используется для поиска данных сессии на сервере, в т.ч. в таблице базы данных. Имеется одно исключение, это дефолтное и рекомендуемое хранилище сессии - CookieStore - которое хранит все данные сессии в куки (ID остается по-прежнему доступным, если он нужен). Преимущества его заключаются в легкости, отсутствии необходимости настройки для нового приложения в порядке использования сессии. Данные в куки криптографически подписаны, что делает их защищенными от взлома. И они также зашифрованы, таким образом любой получивший к ним доступ, не сможет прочитать их содержимое (Rails не примет их, если они были отредактированы).
CookieStore могут хранить около 4 Кбайт данных - намного меньше, чем остальные - но этого обычно хватает. Хранение большего количества данных в сессии не рекомендуется, вне зависимости от того, какое хранилище сессии используется в приложении. Особенно следует избегать хранения в сессии сложных объектов (ничего, кроме простых объектов Ruby, наиболее распространенным примером является экземпляры модели), так как сервер может не собрать их между запросами, что приведет к ошибке.
Если пользовательские сессии не хранят критически важные данные или нет необходимости в ее сохранности на долгий период (скажем, если она используется только для флеш-сообщений), можно рассмотреть использование ActionDispatch::Session::CacheStore
. Он сохранит сессии с использованием реализации кэша, которая была настроена для приложения. Преимущество этого заключается в том, что для хранения сессий можно использовать существующую инфраструктуру кэширования без необходимости дополнительных настроек или администрирования. Недостатком, разумеется, является то, что сессии будут недолговечными и могут исчезнуть в любое время.
Читайте подробнее о хранении сессий в Руководстве по безопасности.
Если вы нуждаетесь в другом механизме хранения сессий, измените его в инициализаторе:
# Use the database for sessions instead of the cookie-based default,
# which shouldn't be used to store highly confidential information
# (create the session table with "rails g active_record:session_migration")
# Rails.application.config.session_store :active_record_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...
NOTE: Изменение secret_key_base при использовании CookieStore
делает все предыдущие сессии невалидными.
Доступ к сессии
В контроллере можно получить доступ к сессии с помощью метода экземпляра session
.
NOTE: Сессии лениво загружаются. Если не получать доступ к сессиям в коде экшна, они не будут загружаться. Следовательно, никогда не придется отключать сессии, просто не обращайтесь к ним, и они будут выполнять свое задание.
Значения сессии хранятся, используя пары ключ/значение, подобно хэшу:
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
end
end
end
Чтобы убрать что-то из сессии, присвойте этому ключу nil
:
class LoginsController < ApplicationController
# "Удаляем" логин (при выходе пользователя)
def destroy
# Убираем id пользователя из сессии
@_current_user = session[:current_user_id] = nil
redirect_to root_url
end
end
Для сброса текущей сессии, используйте reset_session
.
Flash
Flash - это специальная часть сессии, которая очищается с каждым запросом. Это означает, что сохраненные там значения будут доступны только в следующем запросе, что полезно для передачи сообщений об ошибках и т.п.
Доступ к нему можно получить так же, как к сессии, подобно хэшу (это экземпляр FlashHash).
Давайте рассмотрим случай логаута в качестве примера. Контроллер может послать сообщение, которое будет отображено пользователю при следующем запросе:
class LoginsController < ApplicationController
def destroy
session[:current_user_id] = nil
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 было перенесено в другой запрос, используйте метод 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(params[:client])
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(params[:comment])
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 при чтении.
Можно определить, какой сериализатор использовать:
Rails.application.config.action_dispatch.cookies_serializer = :json
Для новых приложений сериализатором по умолчанию является :json
. Для совместимости со старыми приложениями с существующими куки, используется :marshal
, когда не определена опция serializer
.
Также можно установить этой опции :hybrid
, в этом случае Rails десериализует существующие (сериализованные Marshal
) куки при чтении и перезапишет их в формате JSON
. Это полезно при миграции существующих приложений на сериализатор :json
.
Также возможно передать произвольный сериализатор, откликающийся на load
и dump
:
Rails.application.config.action_dispatch.cookies_serializer = MyCustomSerializer
При использовании сериализатора :json
или :hybrid
, следует знать, что не все объекты Ruby могут быть сериализованы как JSON. Например, объекты Date
и Time
будут сериализованы как строки, и у хэшей ключи будут преобразованы в строки.
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
.
Рендеринг данных XML и JSON
ActionController позволяет очень просто рендерить данные 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
.
Фильтры
Фильтры - это методы, которые запускаются "до", "после" или "до и после" экшна контроллера.
Фильтры наследуются, поэтому, если вы установите фильтр в ApplicationController
, он будет запущен в каждом контроллере вашего приложения.
Фильтры "before" могут прерывать цикл запроса. Обычный фильтр "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 # прерывает цикл запроса
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
, которая работает наоборот. Эти опции можно использовать и при добавлении фильтров, поэтому необходимо добавить фильтр, который запускается только для выбранных экшнов в первую очередь.
NOTE: Вызов одного и того же фильтра несколько раз с разными параметрами не будет работать, поскольку последнее определение фильтра перезапишет предыдущие.
After фильтры и around фильтры
В дополнение к фильтрам "before", можно запустить фильтры после того, как экшн был запущен, или "и до, и после".
Фильтры "after" похожи на "before", но поскольку экшн уже был запущен, у них есть доступ к данным отклика, которые будут отосланы клиенту. Очевидно, фильтры "after" не могут остановить экшн от запуска. Обратите внимание, что фильтры "after" выполняются только после успешного выполнения экшна, но не при возникновении исключения в цикле запроса.
Фильтры "around" ответственны за запуск связанных с ними экшнов с помощью 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 методов и использование *_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, и фильтр не запустится в области видимости контроллера. Это не рекомендуемый способ применения такого особого фильтра, но в простых задачах он может быть полезен.
Второй способ - это использовать класс (фактически, подойдет любой объект, реагирующий правильными методами) для управления фильтрацией. Это полезно для более сложных задач, которые не могут быть осуществлены предыдущими двумя способами по причине трудности читаемости и повторного использования. Как пример, можете переписать фильтр авторизации снова, использовав класс:
class ApplicationController < ActionController::Base
before_action LoginFilter
end
class LoginFilter
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
, и так далее. Метод round
должен иметь yield
для исполнения экшна.
Защита от подделки запросов
Подделка межсайтовых запросов (cross-site request forgery) - это тип атаки, в которой сайт обманом заставляет пользователя сделать запрос на другой сайт, возможно, добавляя, изменяя или удаляя данные на этом сайте без ведома или разрешения пользователя.
Первый шаг, чтобы избежать это - убедиться, что все "разрушительные" экшны (создание, обновление и уничтожение) могут быть доступны только не-GET запросам. Если вы следуете соглашениям RESTful, то уже делаете это. Однако, сайт злоумышленника может также легко послать не-GET запрос на ваш сайт, поэтому и необходима защита от подделки запросов. Как сказано в названии, он защищает от подделки запросов.
Это можно сделать, добавив неугадываемый токен, известный только вашему серверу, в каждый запрос. При этом способе, если запрос приходит без подходящего токена, ему будет отказано в доступе.
Если вы генерируете подобную форму:
<%= form_with model: @user, local: true 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.
В Руководстве по безопасности имеется более подробная информация об этом, и множество других вопросов, посвященных безопасности, которые вы должны принимать во внимание при разработке веб-приложения.
(the-request-and-response-objects) Объекты Request и Response
В каждом контроллере есть два accessor-метода, указывающих на объекты запроса и отклика, связанные с циклом запроса, находящегося в текущее время на исполнении. Метод 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 имеется три accessor-метода, которые предоставляют доступ к этим параметрам в зависимости от того, откуда они пришли. Хэш query_parameters
содержит параметры, посланные как часть строки запроса, в то время как хэш request_parameters
содержит параметры, посланные как часть тела post. Хэш path_parameters
содержит параметры, распознанные роутингом как часть пути, ведущего к определенному контроллеру и экшну.
Объект response
Объект response обычно не используется напрямую, но он создается в течение исполнения экшна и рендеринга данных, которые посылаются обратно пользователю, но иногда - например, в последующем фильтре - бывает полезно иметь доступ к отклику напрямую. Некоторые из этих accessor-методов имеют сеттеры, позволяющие изменять их значения. Чтобы получить полный перечень доступных методов, обратитесь к документации по 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"
Note: в вышеприведенном случае более очевидным было бы использование сеттера content_type
.
Аутентификации HTTP
Rails поставляется с двумя встроенными механизмами аутентификации HTTP:
- Простая аутентификация
- Digest аутентификация
Простая аутентификация HTTP
Простая аутентификация HTTP - это аутентификационная схема, поддерживаемая большинством браузеров и других клиентов HTTP. Как пример, рассмотрим раздел администрирования, который доступен только при вводе имени пользователя и пароля в основном диалоговом окне браузера. Использование встроенной аутентификации достаточно простое и требует использования одного метода http_basic_authenticate_with
.
class AdminsController < ApplicationController
http_basic_authenticate_with name: "humbaba", password: "5baa61e4"
end
Благодаря этому можно создавать именованные контроллеры, наследуемые от AdminsController
. Таким образом, предварительный фильтр будет запущен для всех экшнов в этих контроллерах, защищая их с помощью основной аутентификации HTTP
.
Digest аутентификация HTTP
Digest аутентификация HTTP превосходит простую аутентификацию, так как она не требует от клиента посылать незашифрованный пароль по сети (хотя простая аутентификация HTTP безопасна через HTTPS). Использовать digest аутентификацию с 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
вызовет провал аутентификации.
Потоки и загрузка файлов
Иногда хочется послать пользователю файл вместо рендеринга страницы 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
.
WARNING: Будьте осторожны, когда используете данные, пришедшие с клиента (params, куки и т.д.), для обнаружения файла на диске, так как есть риск безопасности в том, что кто-то может получить доступ к файлам, к которым иметь он не должен.
TIP: Не рекомендуется передавать в потоке статичные файлы через Rails, если можно вместо этого разместить их в папке public на веб-сервере. Более эффективно разрешить пользователям скачивать файлы напрямую, используя Apache или другой веб-сервер, сохраняя запрос от ненужного прогона через весь стек Rails.
Загрузка RESTful
Хотя 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
NOTE: Конфигурационные файлы не перезагружаются с каждым запросом, поэтому необходимо перезапустить сервер для того, чтобы изменения вступили в силу.
Теперь пользователь может запрашивать получение версии в PDF, просто добавив ".pdf" в URL:
GET /clients/1.pdf
(Live Streaming of Arbitrary Data) Live Streaming произвольных данных
Rails позволяет отдавать в потоке не только файлы. Фактически, в объекте отклика можно отдать все, что хотите. Модуль ActionController::Live
позволяет создать постоянное соединение с браузером. Используя этот модуль, можно послать в браузер произвольные данные в определенные моменты времени.
Подключение Live Streaming
Включение 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
при использовании потокового отклика. - Серверы WEBrick буферизируют все отклики, поэтому включение
ActionController::Live
не будет работать. Необходимо использовать веб-сервер, не буферизирующий отклики автоматически.
Фильтрация лога
Rails ведет лог-файл для каждой среды в папке log
. Это чрезвычайно полезно при отладке того, что происходит в приложении, но в реальной жизни может быть не нужно хранение каждого бита информации в лог-файле.
Фильтрация параметров
Можно фильтровать конфиденциальные параметры запроса в файлах лога, присоединив их к config.filter_parameters
в настройках приложения. Эти параметры будут помечены в логе как [FILTERED].
config.filter_parameters << :password
NOTE: Предоставленные параметры будут отфильтрованы с помощью частично соответствующего регулярного выражения. По умолчанию Rails добавляет :password
в соответствующем инициализаторе (initializers/filter_parameter_logging.rb
) и заботится о типичных параметрах приложения password
и password_confirmation
.
Фильтрация редиректов
Иногда нужно фильтровать из файлов лога некоторые конфиденциальные ссылки, на которые перенаправляет ваше приложение. Это можно осуществить с использованием конфигурационной опции config.filter_redirect
:
config.filter_redirect << 's3.amazonaws.com'
Ей можно передать строку, регулярное выражение или массив из них.
config.filter_redirect.concat ['s3.amazonaws.com', /private_path/]
Соответствующие URL будут помечены как '[FILTERED]'.
Обработка ошибок
Скорее всего, ваше приложение будет содержать ошибки или, другими словами, вызывать исключения, которые нужно обработать. Например, если пользователь переходит по ссылке на ресурс, который больше не существует в базе данных, Active Record вызовет исключение ActiveRecord::RecordNotFound
.
Дефолтный обработчик исключений Rails отображает сообщение "500 Server Error" для всех исключений. Если запрос сделан локально, отображается прекрасная трассировка и добавляется дополнительная информация, чтобы можно было выяснить, что пошло не так, и разобраться с этим. Если запрос был удаленным, Rails отобразит пользователю лишь простое сообщение "500 Server Error", или "404 Not Found", если была проблема с роутингом или запись не была найдена. Иногда может понадобиться настроить, как эти ошибки будут перехвачены и как они будут отображены пользователю. В приложении на Rails доступны несколько уровней обработки исключений:
Дефолтные шаблоны 500 и 404
По умолчанию приложение в среде 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
WARNING: Использование rescue_from
c Exception
или StandardError
вызовет серьезные побочные эффекты, поскольку это препятствует Rails правильно обрабатывать исключения. Таким образом, это не рекомендуется делать, если нет для того веской причины.
NOTE: При запуске в среде running production все ошибки ActiveRecord::RecordNotFound
рендерят страницу ошибки 404. Если вам не нужно другое поведение, их не нужно обрабатывать.
NOTE: Некоторые исключения перехватываемы только из класса ApplicationController
, так как они вызываются до того, как контроллер будет инициализирован и экшны будут выполнены.
Навязывание протокола HTTPS
Иногда хочется навязать определенному контроллеру быть доступным только через протокол HTTPS по причинам безопасности. Можно использовать в контроллере метод force_ssl
для принуждения к этому:
class DinnerController
force_ssl
end
Подобно фильтру, можно также передать :only
и :except
для обеспечения безопасного соединения только определенным экшнам.
class DinnerController
force_ssl only: :cheeseburger
# или
force_ssl except: :cheeseburger
end
Пожалуйста, отметьте, что если добавить force_ssl
во многие контроллеры, то, возможно, вместо этого можно заставить все приложение использовать HTTPS. В этом случае можно установить config.force_ssl
в файле окружения.