Що таке програмне забезпечення Rack в Ruby? Я не зміг знайти жодного хорошого пояснення того, що вони означають під "програмним забезпеченням".
Що таке програмне забезпечення Rack в Ruby? Я не зміг знайти жодного хорошого пояснення того, що вони означають під "програмним забезпеченням".
Відповіді:
Посереднє програмне забезпечення Rack - це більше, ніж "спосіб фільтрації запиту та відповіді" - це реалізація структури дизайну конвеєра для веб-серверів за допомогою Rack .
Він дуже чітко розмежовує різні етапи обробки запиту - відокремлення питань є ключовою метою всіх добре розроблених програмних продуктів.
Наприклад, у Rack я можу мати окремі етапи роботи конвеєра:
Автентифікація : коли запит надходить, чи правильні дані про вхід користувачів? Як я підтверджую цю OAuth, HTTP Basic Authentication, ім'я / пароль?
Авторизація : "чи користувач уповноважений виконувати цю конкретну задачу?", Тобто захист на основі ролей.
Кешування : чи я вже обробив цей запит, чи можу я повернути кешований результат?
Прикраса : як я можу покращити запит, щоб покращити обробку нижче?
Моніторинг ефективності та використання : які статистичні дані я можу отримати від запиту та відповіді?
Виконання : реально обробити запит та надати відповідь.
Вміння розділяти різні етапи (і необов'язково включати їх) - це чудова допомога в розробці добре структурованих додатків.
Існує також чудова екосистема, що розвивається навколо Rack Middleware - ви повинні мати змогу знайти заздалегідь складені компоненти стійки, щоб виконати всі вищезазначені кроки та багато іншого. Перегляньте вікі Rack GitHub для переліку програмного забезпечення .
Посереднє програмне забезпечення - жахливий термін, який стосується будь-якого програмного компонента / бібліотеки, який допомагає, але безпосередньо не бере участі у виконанні якогось завдання. Дуже поширеними прикладами є реєстрація, аутентифікація та інші загальні компоненти горизонтальної обробки . Це, як правило, речі, які потрібні всім у кількох програмах, але не надто багато людей зацікавлені (або повинні бути) у створенні самих себе.
Коментар про те, що це спосіб фільтрування запитів, ймовірно, походить із епізоду RailsCast 151: Екран програми середнього програмного забезпечення RackCast .
Посереднє програмне забезпечення для стелажів розвинулося з Rack, і в ньому є чудовий вступ у Вступ до програмного забезпечення Rack .
Там в інтро до проміжного в Вікіпедії тут .
Перш за все, Rack - це рівно дві речі:
Rack - Інтерфейс веб-сервера
Самі основи стійки - це проста умова. Кожен веб-сервер, сумісний із стійкою, завжди буде викликати метод виклику на об'єкт, який ви йому надаєте, і обслуговуватиме результат цього методу. Rack вказує, як саме повинен виглядати цей метод виклику та що він повинен повертати. Це стійка.
Давайте спробуємо просто. Я буду використовувати WEBrick як веб-сервер, сумісний із стійкою, але будь-який з них буде робити. Створимо просту веб-програму, яка повертає рядок JSON. Для цього ми створимо файл під назвою config.ru. Конфігурація.ru автоматично буде викликана командною стійкою команди rack gem, яка просто запустить вміст config.ru на веб-сервері, сумісному з стійкою. Тож додамо у файл config.ru наступне:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
Як зазначено в конвенції, наш сервер має метод, який називається call, який приймає хеш середовища і повертає масив із формою [статус, заголовки, тіло] для обслуговування сервера. Давайте спробуємо це, просто зателефонувавши в рейк Сервер, сумісний із стійкою, який може бути встановлений за замовчуванням, можливо, WEBrick або Mongrel запустяться і негайно чекають подання запитів.
$ rackup
[2012-02-19 22:39:26] INFO WEBrick 1.3.1
[2012-02-19 22:39:26] INFO ruby 1.9.3 (2012-01-17) [x86_64-darwin11.2.0]
[2012-02-19 22:39:26] INFO WEBrick::HTTPServer#start: pid=16121 port=9292
Давайте перевіримо наш новий сервер JSON шляхом завивки або відвідування URL-адреси http://localhost:9292/hello.json
та вуаля:
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
Це працює. Чудово! Це основа для кожної веб-рамки, будь то Rails або Sinatra. У якийсь момент вони реалізують метод виклику, опрацьовують весь рамковий код і нарешті повертають відповідь у типовій формі [статус, заголовки, тіло].
Наприклад, у Ruby on Rails стійка запитів потрапляє до ActionDispatch::Routing.Mapper
класу, який виглядає приблизно так:
module ActionDispatch
module Routing
class Mapper
...
def initialize(app, constraints, request)
@app, @constraints, @request = app, constraints, request
end
def matches?(env)
req = @request.new(env)
...
return true
end
def call(env)
matches?(env) ? @app.call(env) : [ 404, {'X-Cascade' => 'pass'}, [] ]
end
...
end
end
Таким чином, Rails перевіряє, залежно від хеш-коду, якщо збігається будь-який маршрут. Якщо це так, він передає env-хеш додатку для обчислення відповіді, інакше він негайно відповідає 404. Отже, будь-який веб-сервер, який відповідає конвенції рейкового інтерфейсу, може обслуговувати повністю продутий додаток Rails.
Посереднє програмне забезпечення
Rack також підтримує створення проміжних шарів. Вони в основному перехоплюють запит, роблять щось з ним і передають його далі. Це дуже корисно для різнобічних завдань.
Скажімо, ми хочемо додати журнал на наш сервер JSON, який також вимірює тривалість запиту. Ми можемо просто створити реєстратор середнього програмного забезпечення, який робить саме це:
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
Коли він створюється, він зберігає собі копію фактичної програми-стійки. У нашому випадку це екземпляр нашого JSONServer. Rack автоматично викликає метод виклику в середньому програмному забезпеченні і очікує повернення [status, headers, body]
масиву, як і наш JSONServer повертається.
Отже, в цьому середньому програмному забезпеченні приймається початкова точка, потім здійснюється фактичний виклик JSONServer @app.call(env)
, потім реєстратор виводить запис журналу і, нарешті, повертає відповідь як [@status, @headers, @body]
.
Щоб наш маленький rackup.ru використовував це проміжне програмне забезпечення, додайте до нього RackLogger так:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
class RackLogger
def initialize(app)
@app = app
end
def call(env)
@start = Time.now
@status, @headers, @body = @app.call(env)
@duration = ((Time.now - @start).to_f * 1000).round(2)
puts "#{env['REQUEST_METHOD']} #{env['REQUEST_PATH']} - Took: #{@duration} ms"
[@status, @headers, @body]
end
end
use RackLogger
map '/hello.json' do
run JSONServer.new
end
Перезавантажте сервер і вуаля, він виводить журнал на кожен запит. Rack дозволяє додавати декілька середніх програм, які викликаються в порядку їх додавання. Це просто чудовий спосіб додати функціональність, не змінюючи ядро додатку стійки.
Стійка - Самоцвіт
Хоча стійка - в першу чергу - умовна умова, вона також є дорогоцінним каменем, який забезпечує чудову функціональність. Один з них ми вже використовували для нашого сервера JSON, команду rackup. Але є більше! Стійка дорогоцінних каменів надає мало додатків для безлічі випадків використання, наприклад, для обслуговування статичних файлів або навіть цілих каталогів. Подивимося, як ми обслуговуємо простий файл, наприклад, дуже основний HTML-файл, розміщений у htmls / index.html:
<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
Ми, можливо, хочемо подати цей файл із кореня веб-сайту, тому додамо наступне на наш config.ru:
map '/' do
run Rack::File.new "htmls/index.html"
end
Якщо ми відвідуємо, http://localhost:9292
ми бачимо, що наш html-файл ідеально відображений. Це було легко, правда?
Додамо цілий каталог файлів javascript, створивши кілька файлів javascript під / javascripts і додавши до config.ru наступне:
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
Перезапустіть сервер і відвідайте, http://localhost:9292/javascript
і ви побачите список усіх файлів javascript, які ви зараз можете включити прямо з будь-якого місця.
У мене була проблема з розумінням Rack себе протягом великої кількості часу. Я це повністю зрозумів лише після роботи над створенням цього мініатюрного веб-сервера Ruby . Я поділився своїми знаннями про Rack (у формі історії) тут, у своєму блозі: http://gauravchande.com/what-is-rack-in-ruby-rails
Відгуки більш ніж вітаються.
config.ru
мінімальний приклад для виконання
app = Proc.new do |env|
[
200,
{
'Content-Type' => 'text/plain'
},
["main\n"]
]
end
class Middleware
def initialize(app)
@app = app
end
def call(env)
@status, @headers, @body = @app.call(env)
[@status, @headers, @body << "Middleware\n"]
end
end
use(Middleware)
run(app)
Бігайте rackup
і відвідуйте localhost:9292
. Вихід:
main
Middleware
Тож зрозуміло, що Middleware
обгортає і називає основний додаток. Тому він може попередньо обробити запит і будь-яким чином опрацювати відповідь.
Як пояснено на веб- сайті: http://guides.rubyonrails.org/rails_on_rack.html#action-dispatcher-middleware-stack , Rails використовує середню програму Rack для великої її функціональності, і ви можете додати свої власні config.middleware.use
сімейні методи.
Перевага впровадження функціональності в проміжне програмне забезпечення полягає в тому, що ви можете повторно використовувати його на будь-якій основі Rack, таким чином, всі основні Ruby, а не лише Rails.
Посереднє програмне забезпечення для стійки - це спосіб фільтрації запиту та відповіді, що надходить у вашу програму. Компонент посередництва знаходиться між клієнтом і сервером, обробляючи вхідні запити та вихідні відповіді, але це більше, ніж інтерфейс, який можна використовувати для спілкування з веб-сервером. Він використовується для групування та замовлення модулів, які зазвичай є класами Ruby, і вказує залежність між ними. Модуль проміжного програмного забезпечення для Rack повинен: - мати конструктор, який приймає наступне додаток у стеці як параметр, - відповідати методу "call", який приймає хеш середовища як параметр. Повертається значення цього виклику - це масив: код стану, хеш середовища та тіло відповіді.
Я використовував програмне забезпечення Rack для вирішення декількох проблем:
Це дало досить елегантні виправлення в обох випадках.
Rack забезпечує мінімальний інтерфейс між веб-серверами, що підтримують рамки Ruby та Ruby.
За допомогою Rack можна написати програму Rack.
Rack передає хеш-середовище (Hash, що міститься в HTTP-запиті від клієнта, що складається з заголовків, схожих на CGI), до вашої програми Rack, яка може використовувати речі, що містяться в цьому хеші, робити все, що завгодно.
Щоб використовувати Rack, ви повинні надати "додаток" - об'єкт, який відповідає #call
методу за допомогою параметра Hash Environment як параметр (як правило, визначений як env
). #call
повинен повернути масив рівно трьох значень:
each
).Ви можете написати програму Rack, яка повертає такий масив - це буде відправлено назад вашому клієнту, Rack, у відповідь (це фактично буде екземпляр класу Rack::Response
[натисніть, щоб перейти до документів]).
gem install rack
config.ru
файл - Rack знає, що це шукати.Ми будемо створювати крихітний Rack додаток , яке повертає відповідь (екземпляр Rack::Response
) , який Відповідний Тіло являє собою масив , який містить рядок: "Hello, World!"
.
Ми запустимо локальний сервер за допомогою команди rackup
.
Відвідавши відповідний порт у нашому браузері, ми побачимо "Привіт, світ!" винесені у вікно перегляду.
#./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, і ви побачите "Привіт, світ!" винесено.
Це не є вичерпним поясненням, але по суті, що тут відбувається, це те, що Клієнт (браузер) надсилає запит HTTP до Rack через ваш локальний сервер, а Rack створює екземпляр MessageApp
та запускcall
, передаючи в середовищі Hash як параметр метод ( env
аргумент).
Rack приймає повернене значення (масив) і використовує його для створення екземпляра Rack::Response
і повертає його назад Клієнту. Браузер використовує магію для друку "Привіт, світ!" на екран.
До речі, якщо ви хочете побачити, як виглядає хеш-середовище, просто покладіть puts env
під нього def call(env)
.
Як мінімум, те, що ви тут написали, - це програма Rack!
У нашому маленькому додатку Rack ми можемо взаємодіяти з env
хешем ( детальніше про хеш навколишнього середовища див. Тут ).
Ми реалізуємо можливість користувача вводити свій власний рядок запиту в URL-адресу, отже, ця рядок буде присутній у запиті HTTP, інкапсульованому як значення в одній з пар ключів / значень хеш середовища.
Наш додаток Rack отримає доступ до цього рядка запиту з хеш-середовища оточення та відправить його назад клієнту (у нашому веб-переглядачі, в даному випадку) через орган у відповіді.
З Документів Rack на хеш-середовищі: "QUERY_STRING: Частина URL-адреси запиту, яка відповідає?, Якщо така є. Може бути порожньою, але завжди обов'язковою!"
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
Тепер, rackup
і відвідайте localhost:9292?hello
( ?hello
це рядок запиту), і вам слід побачити "привіт", відображений у вікні перегляду.
Ми будемо:
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. Це в основному означає, що ви можете створити "стек", що складається з однієї або декількох середніх програм та додатка "нижнього рівня", на який потрібно відправляти. Усі запити, що надходять до вашої програми нижнього рівня, спочатку будуть оброблені вашими проміжними програмами.
#use
вказує проміжне програмне забезпечення для використання в стеку. Посереднє програмне забезпечення приймає як аргумент.
Посереднє програмне забезпечення для стійки повинно:
call
метод, який приймає хеш середовища як параметр.У нашому випадку "Middleware" - MessageSetter
це "конструктор" - initialize
метод MessageSetter , "наступним додатком" в стеці є MessageApp
.
Так ось, через те, що Rack::Builder
робиться під кришкою, app
аргумент методу MessageSetter
's initialize
є MessageApp
.
(обведіть голову навколо вище, перш ніж рухатися далі)
Отже, кожен фрагмент Middleware по суті "передає" існуючий хеш-середовище до наступної програми в ланцюзі - так що у вас є можливість вимкнути цей хеш-середовище в середовищі Middleware, перш ніж передати його наступному додатку в стеку.
#run
бере аргумент, що є об'єктом, який відповідає #call
і повертає Rack Response (екземпляр Rack::Response
).
Використовуючи Rack::Builder
ви можете побудувати ланцюжки Middlewares, і будь-який запит до вашої програми буде оброблятися кожним Middleware по черзі, перш ніж остаточно обробити остаточний фрагмент у стеку (у нашому випадку MessageApp
). Це надзвичайно корисно, оскільки воно розділяє різні етапи обробки запитів. З точки зору "розділення проблем", це не може бути набагато чистішим!
Ви можете побудувати "трубопровід запитів", що складається з декількох середніх програм, які стосуються таких речей, як:
(вище пунктів від іншої відповіді на цю тему)
Ви часто будете бачити це в професійних додатках Sinatra. Sinatra використовує 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
Стійка - Інтерфейс сервера для веб / додатків
Rack - це пакет Ruby, який надає інтерфейс для веб-сервера для зв'язку з програмою. Додавати компоненти проміжного програмного забезпечення між веб-сервером та програмою легко, щоб змінити поведінку вашого запиту / відповіді. Компонент посередництва знаходиться між клієнтом і сервером, обробляючи вхідні запити та вихідні відповіді.
Простіше кажучи, це в основному лише набір вказівок щодо того, як сервер і програма Rails (або будь-який інший веб-додаток Ruby) повинні спілкуватися один з одним .
Щоб використовувати Rack, надайте "додаток": об'єкт, який відповідає методу виклику, приймаючи хеш середовища в якості параметра і повертаючи масив з трьома елементами:
Для отримання додаткового пояснення ви можете перейти за посиланнями нижче.
1. https://rack.github.io/
2. https://redpanthers.co/rack-middleware/
3. https://blog.engineyard.com/2015/understanding-rack-apps-and-middleware
4. https://guides.rubyonrails.org/rails_on_rack.html#resources
У рейках у нас є config.ru як файл стійки, ви можете запустити будь-який файл стійки з rackup
командою. І типовим портом для цього є 9292
. Щоб перевірити це, ви можете просто запустити rackup
у свій каталог рейок і побачити результат. Ви також можете призначити порт, на якому ви хочете його запустити. Команда для запуску файлу стійки на будь-якому конкретному порті є
rackup -p PORT_NUMBER
Rack - це дорогоцінний камінь, який забезпечує простий інтерфейс для абстрактного запиту / відповіді HTTP. Rack знаходиться між веб-рамками (Rails, Sinatra тощо) та веб-серверами (єдиноріг, puma) як адаптер. Зверху зображення зберігає сервер єдинорога повністю незалежно від знань про рейки, а рейки не знають про єдиноріг. Це хороший приклад нещільного з’єднання , розділення проблем .
Зображення вгорі - це конференція, присвячена конференції на рейці https://youtu.be/3PnUV9QzB0g, я рекомендую переглянути її для глибшого розуміння.