Знакомство с Presto — Часть Третья

в 6:24, , рубрики: framework, ruby, Веб-разработка, метки: ,

В данной части приведён список дополнительных 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

Поделиться

* - обязательные к заполнению поля