В данной части приведён список дополнительных HTTP Api опций которые влияют на работу приложения и фреймворка в целом.
Список основных HTTP Api опций приведён в первой части данного туториала.
Вторая часть знакомит с View Api опциями и методами работы с ним.
- http.content_type — HTTP тип содержимого
- http.encoding — устанавливаем кодировку
- http.error — ловим ошибки
- http.before/http.after — выполняем код до и/или после выполнения action-а
- http.auth, http.user — проводим/проверяем аутентификацию пользователей
- http.cache — сохраняем скорость приложения на эталонном уровне
- http.middleware — используем Rack Middleware
http.content_type
— HTTP тип содержимого
Значение по умолчанию: text/html
Можно установить на глобальном уровне, на уровне slice-а/controller-а/action-а или даже на уровне HTTP запроса.
Устанавливается content_type в виде блока, для определённых action-ов или же для всех.
Action-ы передаются в виде аргументов.
Если вызвать без аргументов, установит content_type для всех action-ов.
устанавливаем на глобальном уровне
Presto.http.content_type 'text/plain'
устанавливаем на уровне slice-а
Все action-ы из News и Articles будут возвращать контент Rss типа.
class Ctrl::News
include Presto::Api
http.map :news
end
class Ctrl::Articles
include Presto::Api
http.map :articles
end
app = Presto::App.mount Ctrl do
http.content_type do
http.mime_type '.rss'
end
end
app.run
устанавливаем на уровне controller-а
Все action-ы из News будут возвращать контент HTML типа, тогда как action-ы из RssNews — Rss типа.
class News
include Presto::Api
http.map :news
end
class RssNews
include Presto::Api
http.map :rss_news
http.content_type do
http.mime_type '.rss'
end
end
устанавливаем на уровне action-а
Все action-ы из News будут возвращать контент HTML типа, тогда как #rss и #atom — Rss типа.
class News
include Presto::Api
http.map :news
http.content_type :rss, :atom do
http.mime_type '.rss'
end
def rss
# some logic
end
def atom
# some logic
end
end
устанавливаем в зависимости от HTTP запроса
/news/latest/ будет отображать контент HTML типа.
/news/latest/?type=rss — контент Rss типа.
class News
include Presto::Api
http.map :news
http.content_type do
http.params['type'] == 'rss' ? http.mime_type('.rss') : 'text/html'
end
def latest
# some logic
end
end
оглавление↑
http.encoding
— устанавливаем кодировку
Значение по умолчанию: nil
Устанавливаться аналогично http.content_type.
Тоже на глобальном уровне, на уровне slice-а/controller-а/action-а или HTTP запроса.
Тоже в виде блока, для определённых action-ов или же для всех.
устанавливаем на глобальном уровне
Presto.http.encoding 'UTF-8'
в зависимости от HTTP запроса
class News
include Presto::Api
http.map
http.encoding do
http.params['country'] == 'jp' ? 'Shift-JIS' : 'UTF-8'
end
end
оглавление↑
http.error
— ловим ошибки
Значение по умолчанию: Уродливая страница с сообщением об ошибке.
http.error позволяет в случае ошибок выводить страницу соответвующая дизайну вашего сайта.
Можно установить на уровне slice-а или controller-а.
404(страница не найдена) на уровне controller-а
class News
include Presto::Api
http.map
http.error 404 do
template = get_template_by_supported_language()
view.render_view template || 'default-404-page'
end
end
500(фатальная ошибка) на уровне slice-а
module MyApp
class Controller
include Presto::Api
http.map
end
end
app = Presto::App.mount MyApp do
http.error 500 do |exception|
@error = exception_to_human_readable_error(exception)
view.render_view 'error-500'
end
end
app.run
оглавление↑
http.before/http.after
— выполняем код до и/или после выполнения action-а
Значение по умолчанию: nil
Можно установить на уровне slice-а, controller-а или action-а.
Action-ы передаются в виде аргументов.
Если вызвать без аргументов, установит callback для всех action-ов.
вычисляем сколько времени тратит каждый action
class Controller
include Presto::Api
http.map
http.before do
@started_at = Time.now.to_f
end
http.after do
puts " - #{ http.action } consumed #{ Time.now.to_f - @started_at } milliseconds"
end
end
Извлекаем что-то из БД перед
#edit, #update, #delete
— мы же ленивые и не станем повторять одну и ту же операцию в каждом action-е
@item
будет доступен каждому из #edit, #update, #delete
, но не другим action-ам.
http.before :edit, :update, :delete do |id|
@item = Model.first(id: id.to_i)
end
def edit id
# some logic
end
def update id
# some logic
end
def delete id
# some logic
end
оглавление↑
http.auth, http.user
— проводим/проверяем аутентификацию пользователей
Скажи пароль! мм… Пароль...
Установливается на уровне slice-а, controller-а или action-а.
Action-ы передаются в виде аргументов.
Если вызвать без аргументов, все action-ы будут требовать аутентификацию.
Поддерживаются следующие типы аутентификаций:
- Basic
- Digest
- HTML
Если тип не задан, используется Basic.
Важно При любом типе авторизации, если она прошла успешно, авторизированный пользователь будет доступен через http.user
Если же авторизация не прошла или не запрашивалась — http.user
всегда nil.
Basic. Самый простой в использовании, но также самый уязвимый
— пароль передаётся в НЕ-зашифрованном виде
Пример: Всё что движется под Admin-ом будет запрашивать аутентификацию.
controller Admin
include Presto::Api
http.map
http.auth do |user, pass|
user == "admin" && Digest::MD5.hexdigest(pass) == "md5 хэш вашего пароля"
end
end
Пример: Только #my_bikini_photos будет запрашивать аутентификацию.
controller MyBlog
include Presto::Api
http.map
http.auth :my_bikini_photos do |user, pass|
user == "admin" && pass == "super-secret-password"
end
def my_bikini_photos
# HTML containing top secret photos
end
end
Digest. Также простой в использовании, но более безопасный
— пароль передаётся в зашифрованном виде
Требуется дополнительное действие — вы должны зашифровать пароль используемый в приложении.
Пароль формируется из username, realm и password, разделённые двоеточием и зашифрованном посредством md5.
Пример: username — admin, realm — AccessRestricted, password — super-secret-passowrd
Digest::MD5.hexdigest "admin:AccessRestricted:super-secret-passowrd"
=> "4b17e1bc2219039366bca9c913e88073"
Зашифруете пароль таким образом где-то на стороне, скажем в IRB, и полученный хэш используете в приложении.
Блок должен возвращать зашифрованный пароль введённого username-a.
Пример: Создаём slice Admin и закрываем его на ключ, используя Digest аутентификацию
controller Admin
class Products
include Presto::Api
http.map :products
end
class Orders
include Presto::Api
http.map :orders
end
end
app = Presto::App.mount Admin do
http.auth type: :digest, realm: "AccessRestricted" do |user|
users = { 'admin' => "md5 хэш состоящий из username:AccessRestricted:password" }
users[user]
end
end
Примечание: realm используемый в опциях(http.auth type: :digest, realm: "AccessRestricted"
)
должен совпадать с realm-ом который использовался при шифровки пароля.
HTML. Относительный уровень безопасности
— пароль передаётся в зашифрованном виде
Позволяет отображать HTML страницу которая соответствует дизайну вашего сайта.
Страница должна включать PRESTO_AUTH_HTML_FORM
[String] в месте где должна отображаться форма авторизации.
Блок должен возвращать пароль пользователя в виде md5 хэша.
То есть шифруем пароль где-то на стороне, скажем в IRB, и полученный хэш используем в приложении.
Digest::MD5.hexdigest 'super-secret-password'
=> "e8ffbb3d19a8f59fba9da193a86e7061"
app.rb
controller MyBlog
include Presto::Api
http.map
http.auth :type => :html, :layout => view.render_view("path/to/my-template") do |user|
{'admin' => 'e8ffbb3d19a8f59fba9da193a86e7061'}[user]
end
end
path/to/my-template.erb
<h1>Header</h1>
<!-- форма авторизации -->
PRESTO_AUTH_HTML_FORM
<!-- / форма авторизации -->
<h2>Footer</h2>
PRESTO_AUTH_HTML_FORM
это просто строка которая будет заменена на форму авторизации.
Встроенная форма выглядет примерно так:
<form ... class="presto_auth_html_form">
<input ... class="presto_auth_html_username"/>
<input ... class="presto_auth_html_password"/>
<input ... class="presto_auth_html_submit"/>
</form>
Как видете, можно полностью видоизменить форму посредством CSS классов.
http.reset_auth позволяет отменить текущую авторизацию.
Работает ресет только для HTML Auth.
Важно: По умолчанию, аторизированные пользователи хранятся в оперативной памяти.
Если хотите чтобы они хранились в HTTP sessions, используете опцию pool, передав :session [Symbol] в качестве pool-а:
http.auth :type => :html, pool: :session do |user|
{'admin' => 'md5 of your password'}[user]
end
Можно также использовать MongoDB:
db = Mongo::Connection.new.db('auth')
http.auth :type => :html, pool: Presto::Cache::MongoDB.new(db) do |user|
{'admin' => 'md5 of your password'}[user]
end
оглавление↑
http.cache
— сохраняем скорость приложения на эталонном уровне
Позволяет кэшировать результат выполнения action-а и пропускать его выполнение при следующих запросах.
Стоит отметить что даже при включённом кэше, before/after callback-и выполняются.
Так что если собираетесь использовать кэш, уберите из callback-ов весь «медленный» код,
на подобие запросов к БД или к разным сервисам.
Кэш можно включить на уровне slice-a, controller-a или action-а.
Для этого нужно вызвать http.cache
, с аргументами или без, но с обязательном блоком.
Блок будет решать при каких запросах использовать кэш, при каких не использовать, и при каких обновить.
- Кэш используется если блок возвращает true [Boolean] или же любое non-nil / non-false значение.
- Если блок возвращает :update [Symbol], кэш текущего action-а обновляется.
- Если блок возвращает :purge или :truncate [Symbol], кэш всех action-ов данного controller-а обновляется.
- И соответственно, если блок вернёт nil или false, кэш игнорируется и action выполняется.
Пример: все action-ы используют кэш
class App
include Presto::Api
# basic setup
http.cache { true }
end
Пример: только :details и :features используют кэш
class App
include Presto::Api
# basic setup
http.cache :details, :features do
true
end
end
Пример: все action-ы используют кэш, обновляя его если в HTTP параметрах обнаружена переменная «rebuild-cache»
class App
include Presto::Api
# basic setup
http.cache do
http.params['rebuild-cache'] ? :update : true
end
end
Важно отметить что блок выполняется при каждом запросе, так что логика в нём должна быть наипростейшей.
Логика блока должна занимать намного меньше одной миллисекунды, иначе, кэширование не имеет смысла.
Подробнее насчёт хранилища
По умолчанию кэш хранится в оперативной памяти.
Быстро и Удобно.
Но в некоторых случаях разумнее использовать быстрый DB, скажем MongoDB.
В таком случае устанавливаем хранилище посредством http.cache_pool
class News
include Presto::Api
# basic setup
db = Mongo::Connection.new('localhost').db('db-name')
http.cache_pool Presto::Cache::MongoDB.new(db)
http.cache { true }
end
оглавление↑
http.middleware
— используем Rack Middleware
Middleware можно установить на уровне приложения, slice-а или controller-а.
http.use
принимает любые аргументы которые нужны при использовании middleware.
установливаем на уровне приложения
app = Presto::App.new
app.use SomeMiddleware, 'with', :some => :opts
установливаем на уровне slice-а
class Forum
include Presto::Api
http.map
end
app = Presto::App.new
app.mount Forum do
http.use Rack::CommonLogger
end
установливаем на уровне controllera-а
class Forum
include Presto::Api
http.map
http.use Rack::CommonLogger
end
На этом про HTTP опции (пока) всё.
В следующей части поговорим о методах работы с HTTP Api,
таких как http.params, http.session, http.cookies, http.redirect, http.pass etc.
Автор: slivu