Вопрос

Что такое промежуточное программное обеспечение Rack в Ruby?Я не смог найти никакого хорошего объяснения тому, что они подразумевают под "промежуточным программным обеспечением".

Это было полезно?

Решение

Стойка как дизайн

Промежуточное программное обеспечение Rack - это нечто большее, чем "способ фильтрации запроса и ответа" - это реализация схема проектирования трубопровода для веб-серверов, использующих Стойка.

Он очень четко разделяет различные этапы обработки запроса - разделение проблем является ключевой целью всех хорошо разработанных программных продуктов.

Например, с помощью Rack я могу выполнять отдельные этапы конвейера:

  • Аутентификация:когда поступает запрос, верны ли данные пользователя для входа в систему?Как мне проверить этот OAuth, базовую аутентификацию HTTP, имя / пароль?

  • Авторизация:"авторизован ли пользователь для выполнения этой конкретной задачи?", т.е.безопасность на основе ролей.

  • Кэширование:обработал ли я уже этот запрос, могу ли я вернуть кэшированный результат?

  • Украшение:как я могу улучшить запрос, чтобы улучшить последующую обработку?

  • Мониторинг производительности и использования:какую статистику я могу получить по запросу и ответу?

  • Исполнение:фактически обработайте запрос и предоставьте ответ.

Возможность разделить различные этапы (и при необходимости включить их) является большим подспорьем в разработке хорошо структурированных приложений.

Сообщество

Существует также отличная экосистема, разрабатываемая на основе промежуточного программного обеспечения Rack - вы должны быть в состоянии найти готовые компоненты rack для выполнения всех описанных выше шагов и многого другого.Видишь список промежуточного программного обеспечения можно найти в Rack GitHub wiki.

Что такое промежуточное программное обеспечение?

Промежуточное программное обеспечение - это ужасный термин, который относится к любому программному компоненту / библиотеке, которая помогает, но не участвует непосредственно в выполнении какой-либо задачи.Очень распространенными примерами являются ведение журнала, аутентификация и другие обычные горизонтальные обрабатывающие компоненты.Как правило, это то, что нужно каждому в нескольких приложениях, но не слишком много людей заинтересованы (или должны быть заинтересованы) в самостоятельном создании.

Дополнительная информация

Другие советы

Прежде всего, Стойка - это ровно две вещи:

  • Соглашение об интерфейсе веб-сервера
  • Драгоценный камень

Rack - Интерфейс веб - сервера

Самые основы rack - это простое соглашение.Каждый веб-сервер, совместимый с rack, всегда будет вызывать метод call для объекта, который вы ему предоставляете, и обслуживать результат этого метода.Rack точно определяет, как должен выглядеть этот вызываемый метод и что он должен возвращать.Это дыба.

Давайте попробуем это сделать просто.Я буду использовать WEBrick в качестве веб-сервера, совместимого со стойкой, но подойдет любой из них.Давайте создадим простое веб-приложение, которое возвращает строку JSON.Для этого мы создадим файл с именем config.ru .config.ru Будет автоматически вызван командой rackup rackup для rackup, которая просто запустит содержимое config.ru на веб-сервере, совместимом с rack.Так давайте добавим в файл следующий код 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, который принимает хэш среды и возвращает массив с формой [status, headers, body] для обслуживания веб-сервером.Давайте попробуем это, просто вызвав rackup.Сервер, совместимый со стойкой по умолчанию, возможно 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 запросы rack попадают в 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, совпадает ли какой-либо маршрут.Если это так, он передает хэш env приложению для вычисления ответа, в противном случае оно немедленно выдает 404.Таким образом, любой веб-сервер, совместимый с соглашением об интерфейсе rack, способен обслуживать полностью развернутое приложение 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

Когда он создается, он сохраняет себе копию фактического приложения rack.В нашем случае это экземпляр нашего JSONServer.Rack автоматически вызывает метод call в промежуточном программном обеспечении и ожидает в ответ [status, headers, body] массив, точно такой же, как возвращает наш JSONServer.

Итак, в этом промежуточном программном обеспечении берется начальная точка, затем выполняется фактический вызов JSONServer с помощью @app.call(env), затем регистратор выводит запись в журнале и , наконец, возвращает ответ в виде [@status, @headers, @body].

Чтобы сделать наш маленький rackup.если вы используете это промежуточное программное обеспечение, добавьте к нему use 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 позволяет добавлять несколько промежуточных программ, которые вызываются в порядке их добавления.Это просто отличный способ добавить функциональность без изменения ядра приложения rack.

Стойка - Жемчужина

Хотя rack - прежде всего - это условность, он также является драгоценным камнем, обеспечивающим отличную функциональность.Один из них мы уже использовали для нашего JSON-сервера, команду rackup.Но это еще не все!Rack gem предоставляет небольшие приложения для множества вариантов использования, таких как обслуживание статических файлов или даже целых каталогов.Давайте посмотрим, как мы обслуживаем простой файл, например, очень простой 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, которые теперь вы можете включить прямо из любого места.

Долгое время у меня была проблема с пониманием самого себя.Я полностью понял это только после работы над созданием этого миниатюрный веб-сервер 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 middlewares для большей части своих функциональных возможностей, и вы также можете добавить свои собственные с config.middleware.use семейные методы.

Преимущество реализации функциональности в промежуточном программном обеспечении заключается в том, что вы можете повторно использовать его на любой платформе Rack, таким образом, на всех основных платформах Ruby, а не только Rails.

Промежуточное программное обеспечение Rack - это способ фильтрации запросов и ответов, поступающих в ваше приложение.Компонент промежуточного программного обеспечения находится между клиентом и сервером, обрабатывая входящие запросы и исходящие ответы, но это больше, чем интерфейс, который можно использовать для взаимодействия с веб-сервером.Он используется для группировки и упорядочивания модулей, которые обычно являются классами Ruby, и определения зависимости между ними.Модуль промежуточного программного обеспечения стойки должен содержать только:– иметь конструктор, который принимает следующее приложение в стеке в качестве параметра – отвечать на метод “call”, который принимает хэш среды в качестве параметра.Возвращаемое значение из этого вызова представляет собой массив:код состояния, хэш среды и тело ответа.

Я использовал промежуточное программное обеспечение Rack для решения пары проблем:

  1. Улавливание ошибок синтаксического анализа JSON с помощью пользовательского промежуточного программного обеспечения Rack и возвращает красиво отформатированные сообщения об ошибках, когда клиент отправляет поврежденный JSON
  2. Сжатие содержимого с помощью стойки::Дефлатер

Это позволяло довольно элегантно исправить ситуацию в обоих случаях.

Что такое Стойка?

Rack обеспечивает минимальный интерфейс между веб-серверами, поддерживающими Ruby, и Ruby framework.

Используя Rack, вы можете написать приложение Rack.

Rack передаст хэш среды (хэш, содержащийся внутри HTTP-запроса от клиента и состоящий из CGI-подобных заголовков) вашему приложению Rack, которое может использовать содержимое этого хэша для выполнения всего, что захочет.

Что такое стоечное приложение?

Чтобы использовать Rack, вы должны предоставить "приложение" - объект, который реагирует на #call метод с хэшем среды в качестве параметра (обычно определяется как env). #call должен возвращать массив ровно из трех значений:

  • в Код состояния (например, '200'),
  • a Хэш заголовков,
  • в Орган реагирования (который должен реагировать на метод Ruby, each).

Вы можете написать приложение Rack, которое возвращает такой массив - он будет отправлен обратно вашему клиенту с помощью Rack внутри Ответ (на самом деле это будет экземпляр из Класса Rack::Response [нажмите, чтобы перейти к документам]).

Очень простое приложение для установки в стойку:

  • gem install rack
  • Создать config.ru file - 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 и посетить локальный хост:9292 и вы бы видели, как отображается надпись "Привет, мир!".

Это не исчерпывающее объяснение, но, по сути, здесь происходит то, что клиент (браузер) отправляет HTTP-запрос в Rack через ваш локальный сервер, и Rack создает экземпляры MessageApp и бежит call, передавая хэш среды в качестве параметра в метод (the env аргумент).

Rack принимает возвращаемое значение (массив) и использует его для создания экземпляра Rack::Response и отправляет это обратно Клиенту.Браузер использует Магия чтобы вывести "Привет, мир!" на экран.

Кстати, если вы хотите посмотреть, как выглядит хэш среды, просто поместите puts env под ним def call(env).

Каким бы минимальным оно ни было, то, что вы здесь написали, - это приложение для установки в стойку!

Как заставить приложение Rack взаимодействовать с хэшем входящей среды

В нашем маленьком приложении Rack мы можем взаимодействовать с env хэш (см. здесь подробнее о хэше среды).

Мы реализуем возможность для пользователя вводить свою собственную строку запроса в URL-адрес, следовательно, эта строка будет присутствовать в HTTP-запросе, инкапсулированная как значение в одной из пар ключ / значение хэша среды.

Наше приложение Rack получит доступ к этой строке запроса из хэша среды и отправит ее обратно клиенту (в данном случае нашему браузеру) через Тело ответа.

Из документации Rack по хэшу среды:"СТРОКА ЗАПРОСА:Часть URL-адреса запроса, которая следует за ?, если таковая имеется.Может быть пустым, но всегда требуется!"

#./message_app.rb
class MessageApp
  def call(env)
    message = env['QUERY_STRING']
    [200, {}, [message]]
  end
end

Сейчас, rackup и посетить localhost:9292?hello (?hello является строкой запроса), и вы должны увидеть 'hello', отображаемое в окне просмотра.

Промежуточное программное обеспечение стойки

Мы будем:

  • вставьте часть промежуточного программного обеспечения Rack в нашу кодовую базу - класс a: 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 реализует небольшой DSL для итеративного построения стоечных приложений.По сути, это означает, что вы можете создать "Стек", состоящий из одного или нескольких промежуточных программ и приложения "нижнего уровня" для отправки.Все запросы, поступающие в ваше приложение нижнего уровня, будут сначала обработаны вашим промежуточным программным обеспечением (программами).

#use указывает промежуточное программное обеспечение для использования в стеке.Он принимает промежуточное программное обеспечение в качестве аргумента.

Промежуточное программное обеспечение стойки должно:

  • имейте конструктор, который принимает следующее приложение в стеке в качестве параметра.
  • реагировать на call метод, который принимает хэш среды в качестве параметра.

В нашем случае "промежуточным программным обеспечением" является MessageSetter, "конструктор" - это messagesetter's initialize метод, "следующее приложение" в стеке - это MessageApp.

Итак, вот из-за чего Rack::Builder делает ли под капотом, app аргумент о MessageSetter's initialize метод заключается в MessageApp.

(обдумайте вышесказанное, прежде чем двигаться дальше)

Следовательно, каждая часть промежуточного программного обеспечения по существу "передает" существующий хэш среды следующему приложению в цепочке - так что у вас есть возможность изменить этот хэш среды в промежуточном программном обеспечении, прежде чем передавать его следующему приложению в стеке.

#run принимает аргумент, представляющий собой объект , который отвечает на #call и возвращает ответ Rack (экземпляр Rack::Response).

Выводы

Используя Rack::Builder вы можете создавать цепочки промежуточных программ, и любой запрос к вашему приложению будет обрабатываться каждым промежуточным программным обеспечением по очереди, прежде чем, наконец, будет обработан последней частью в стеке (в нашем случае, MessageApp).Это чрезвычайно полезно, поскольку позволяет разделить различные этапы обработки запросов.С точки зрения "разделения забот", это не могло быть намного чище!

Вы можете создать "конвейер запросов", состоящий из нескольких промежуточных программ, которые имеют дело с такими вещами, как:

  • Аутентификация
  • Авторизация
  • Кэширование
  • Украшение
  • Мониторинг производительности и использования
  • Выполнение (фактически обрабатывает запрос и предоставляет ответ)

(выше указаны пункты из другого ответа в этой теме)

Вы часто увидите это в профессиональных приложениях Sinatra.Синатра использует Стойку!Видишь здесь для определения того, что такое Синатра ЯВЛЯЕТСЯ!

В качестве заключительного замечания, наш 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 - Интерфейсный черно-белый веб-сервер и сервер приложений

Rack - это пакет Ruby, который предоставляет веб-серверу интерфейс для взаимодействия с приложением.Легко добавить компоненты промежуточного программного обеспечения между веб-сервером и приложением, чтобы изменить поведение вашего запроса / ответа.Компонент промежуточного программного обеспечения находится между клиентом и сервером, обрабатывая входящие запросы и исходящие ответы.

Проще говоря, это в основном просто набор рекомендаций о том, как сервер и приложение Rails (или любое другое веб-приложение Ruby) должны взаимодействовать друг с другом.

Чтобы использовать Rack, предоставьте "приложение":объект, который отвечает на метод вызова, принимая хэш среды в качестве параметра и возвращая массив с тремя элементами:

  • Код ответа HTTP
  • Хэш заголовков
  • В орган реагирования, который должен реагировать на каждый запрос.

Для получения более подробных объяснений вы можете перейти по ссылкам ниже.

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

В rails у нас есть config.ru в качестве файла rack вы можете запустить любой файл rack с rackup команда.И порт по умолчанию для этого является 9292.Чтобы проверить это, вы можете просто запустить rackup в вашем каталоге rails и посмотрите результат.Вы также можете назначить порт, на котором вы хотите его запустить.Команда для запуска файла rack на любом конкретном порту - это

rackup -p PORT_NUMBER
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top