Что такое стойка?
Rack обеспечивает минимальный интерфейс между веб-серверами, поддерживающими Ruby и платформы Ruby.
Используя Rack, вы можете написать приложение Rack.
Rack передаст хэш Environment (хэш, содержащийся внутри HTTP-запроса от клиента, состоящего из CGI-подобных заголовков) вашему приложению Rack, которое может использовать вещи, содержащиеся в этом хэш-функции, для выполнения чего угодно.
Что такое приложение для установки в стойку?
Чтобы использовать Rack, вы должны предоставить «приложение» - объект, который отвечает на метод #call
с хэшом среды в качестве параметра (обычно определяется как env
). #call
должен возвращать массив из трех значений:
- Код состояния (например, «200»),
- a Хеш заголовков ,
- Тело ответа (которое должно отвечать методу Ruby,
each
).
Вы можете написать приложение Rack, которое возвращает такой массив - оно будет отправлено вашему клиенту Rack внутри Response (на самом деле это будет экземпляр из Класс Rack::Response
[нажмите, чтобы перейти к документам]).
Очень простое приложение для стойки:
- Создайте файл
config.ru
- Rack знает, как искать это.
Мы создадим крошечное приложение Rack, которое возвращает ответ (экземпляр Rack::Response
), чье тело ответа представляет собой массив, содержащий строку: "Hello, World!"
.
Мы запустим локальный сервер с помощью команды rackup
.
При посещении соответствующего порта в нашем браузере мы увидим «Hello, World!» отображается в окне просмотра.
#./message_app.rb
class MessageApp
def call(env)
[200, {}, ['Hello, World!']]
end
end
#./config.ru
require_relative './message_app'
run MessageApp.new
Запустите локальный сервер с rackup
и посетите localhost: 9292 , и вы должны увидеть «Hello, World!» оказаны.
Это не исчерпывающее объяснение, но, по сути, здесь происходит то, что Клиент (браузер) отправляет HTTP-запрос Rack через ваш локальный сервер, а Rack создает экземпляр MessageApp
и запускает call
, передавая Хэш среды как параметр метода (аргумент env
).
Rack принимает возвращаемое значение (массив) и использует его для создания экземпляра Rack::Response
и отправляет его обратно клиенту. Браузер использует magic для печати «Hello, World!» на экран.
Кстати, если вы хотите посмотреть, как выглядит хеш среды, просто поставьте puts env
под def call(env)
.
Как минимум, то, что вы написали здесь, является приложением Rack!
Взаимодействие стоечного приложения с хэшем Incoming Environment
В нашем маленьком приложении Rack мы можем взаимодействовать с хешем env
(см. здесь для получения дополнительной информации о хэше Environment).
Мы реализуем возможность для пользователя вводить свою собственную строку запроса в URL, следовательно, эта строка будет присутствовать в HTTP-запросе, инкапсулируясь в качестве значения в одну из пар ключ / значение хэша Environment.
Наше приложение Rack получит доступ к этой строке запроса из хэша Environment и отправит ее обратно клиенту (в нашем случае, нашему браузеру) через тело в ответе.
Из стойки документов по хешу окружающей среды:
"QUERY_STRING: часть URL запроса, которая следует за?, Если есть. Может быть пустой, но всегда обязательна!"
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
Теперь, rackup
и зайдите localhost:9292?hello
(?hello
- строка запроса), и вы должны увидеть 'hello' в окне просмотра.
Промежуточное программное обеспечение для стойки
Мы будем:
- вставьте часть Rack Middleware в нашу кодовую базу - класс:
MessageSetter
,
- Хеш окружения сначала попадет в этот класс и будет передан в качестве параметра:
env
,
MessageSetter
вставит ключ 'MESSAGE'
в хеш env, его значение будет 'Hello, World!'
, если env['QUERY_STRING']
пусто; env['QUERY_STRING']
если нет,
- наконец, он вернет
@app.call(env)
- @app
как следующее приложение в стеке: MessageApp
.
Во-первых, версия для длинных рук:
#./middleware/message_setter.rb
class MessageSetter
def initialize(app)
@app = app
end
def call(env)
if env['QUERY_STRING'].empty?
env['MESSAGE'] = 'Hello, World!'
else
env['MESSAGE'] = env['QUERY_STRING']
end
@app.call(env)
end
end
#./message_app.rb (same as before)
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
#config.ru
require_relative './message_app'
require_relative './middleware/message_setter'
app = Rack::Builder.new do
use MessageSetter
run MessageApp.new
end
run app
Из документов Rack :: Builder мы видим, что Rack::Builder
реализует небольшой DSL для итеративного конструирования приложений Rack. В основном это означает, что вы можете создать «стек», состоящий из одного или нескольких промежуточных программ и приложения «нижнего уровня» для отправки. Все запросы, поступающие в ваше приложение нижнего уровня, будут сначала обрабатываться вашим Middleware (s).
#use
указывает промежуточное программное обеспечение для использования в стеке. В качестве аргумента используется промежуточное программное обеспечение.
Промежуточное программное обеспечение стойки должно:
- имеет конструктор, который принимает следующее приложение в стеке в качестве параметра.
- отвечает на метод
call
, который принимает хэш Environment в качестве параметра.
В нашем случае «Middleware» - это MessageSetter
, «конструктором» является метод initialize
MessageSetter, «следующим приложением» в стеке является MessageApp
.
Итак, из-за того, что Rack::Builder
делает под капотом, аргумент app
метода MessageSetter
initialize
- MessageApp
.
(обдумайте вышесказанное, прежде чем двигаться дальше)
Таким образом, каждый компонент Middleware, по сути, «передает» существующий хэш среды следующему приложению в цепочке, поэтому у вас есть возможность изменить этот хэш среды в Middleware, прежде чем передать его следующему приложению в стеке. .
#run
принимает аргумент, который является объектом, который отвечает на #call
и возвращает Ответ стойки (экземпляр Rack::Response
).
Выводы
Используя Rack::Builder
, вы можете создавать цепочки промежуточного программного обеспечения, и любой запрос к вашему приложению будет обрабатываться каждым промежуточным программным обеспечением по очереди, прежде чем окончательно обрабатывается последней частью стека (в нашем случае, MessageApp
). Это чрезвычайно полезно, поскольку разделяет различные этапы обработки запросов. С точки зрения «разделения интересов», это не может быть намного чище!
Вы можете создать «конвейер запросов», состоящий из нескольких промежуточных программ, которые имеют дело с такими вещами, как:
- Аутентификация
- Авторизация
- Кэширование
- украшение
- Мониторинг производительности и использования
- Выполнение (фактически обработать запрос и предоставить ответ)
(выше пул из другого ответа в этой теме)
Вы часто будете видеть это в профессиональных приложениях Синатры. Синатра использует Rack! См. здесь для определения того, что Синатра IS !
В качестве заключительного замечания, наш config.ru
может быть написан в стиле сокращения, производя точно такую же функциональность (и это то, что вы обычно видите):
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
И чтобы более четко показать, что делает MessageApp
, вот его версия "длинной руки", которая явно показывает, что #call
создает новый экземпляр Rack::Response
с необходимыми тремя аргументами.
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
Полезные ссылки