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

238

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

  • 4
    Существует также руководство по RailsGuide, которое теперь полностью охватывает Rack, включая промежуточное программное обеспечение: guides.rubyonrails.org/rails_on_rack.html
  • 0
    Большое спасибо команде PhusionPassenger, у них есть хорошо объясненная статья в их блоге. rubyraptor.org/...
Теги:
http
web-applications
rack

7 ответов

316
Лучший ответ

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

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

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

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

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

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

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

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

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

  • Выполнение: действительно обрабатывать запрос и предоставлять ответ.

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

Основной

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

Что такое Middleware?

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

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

  • 0
    Одна вещь, по которой мне неясно: все ли промежуточные программы используют одни и те же данные? Можно ли их разделить (т.е. песочницу один) для безопасности?
  • 2
    Rack является частью вашего приложения, поэтому все промежуточное программное обеспечение составляет одну и ту же копию запроса, и каждый может изменить ее так, как хочет. AFAIK, нет никакого способа изолировать их друг от друга так же, как нет способа изолировать один объект от другого в рамках одного и того же процесса (несмотря на попытки песочницы Ruby).
Показать ещё 2 комментария
62

Прежде всего, Rack - это ровно две вещи:

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

Стойка - интерфейс веб-сервера

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

Давайте попробуем простую попытку. Я буду использовать WEBrick в качестве стойкого веб-сервера, но любой из них будет делать. Позвольте создать простое веб-приложение, которое возвращает строку JSON. Для этого мы создадим файл config.ru. Config.ru будет автоматически вызываться консольной консолью, которая будет просто запускать содержимое 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

Как указано в соглашении, наш сервер имеет метод, называемый вызовом, который принимает хеш среды и возвращает массив с формой [status, headers, body] для обслуживающего веб-сервера. Попробуйте это, просто позвонив в rawn. Сервер, совместимый с стойкой по умолчанию, может быть, 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 и voila:

$ 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, если какой-либо маршрут совпадает. Если это так, то передается env хэш на приложение для вычисления ответа, в противном случае он сразу отвечает 404. Таким образом, любой веб-сервер, который соответствует стандарту интерфейса стойки, может обслуживать полностью взорванное приложение Rails.

Middleware

Стойка также поддерживает создание промежуточных слоев. Они в основном перехватывают запрос, делают с ним что-то и передают его. Это очень полезно для универсальных задач.

Скажем, мы хотим добавить журнал на наш 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   

Перезагрузите сервер и voila, он выводит журнал на каждый запрос. Стойка позволяет добавлять несколько посредников, вызываемых в том порядке, в котором они добавлены. Это просто отличный способ добавить функциональность, не меняя ядро ​​приложения стойки.

Стойка - драгоценный камень

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

  • 3
    Но не промежуточное программное обеспечение стойки?
  • 3
    Вы правы, спасибо. Я добавил раздел промежуточного программного обеспечения сейчас.
Показать ещё 2 комментария
18

У меня возникла проблема с пониманием того, что я считаю себя очень хорошим временем. Я только полностью понял это после работы над этим миниатюрным веб-сервером . Я поделился своими знаниями о Rack (в форме истории) здесь, в моем блоге: http://gauravchande.com/what-is-rack-in-ruby-rails

Обратная связь более чем приветствуется.

  • 11
    Ответы, содержащие только ссылки, не рекомендуется использовать при переполнении стека , поскольку если ресурс, на который идет ссылка, станет недоступным в будущем, ответ станет бесполезным. Пожалуйста, по крайней мере, суммируйте соответствующие пункты вашего блога и добавьте их к этому ответу.
  • 0
    Спасибо за ваш пост. Я очень начинающий программист на Rails, и я понял концепцию стоек из вашего чистого поста.
Показать ещё 2 комментария
5

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

5

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.

4

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

В обоих случаях он предоставлял довольно элегантные исправления.

  • 2
    Этот ответ, хотя и несколько полезный, на самом деле не затрагивает вопрос о том, что такое Rack Middleware .
  • 0
    Также этот ответ довольно просто ссылка ...: P
3

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

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

С помощью Rack вы можете написать приложение для стойки.

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

Что такое приложение для стойки?

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

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

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

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

  • gem install rack
  • Создайте файл config.ru - стойка знает, что нужно искать.

Мы создадим крошечное приложение Rack, которое возвращает Response (экземпляр Rack::Response), который Response Body - это массив, содержащий строку String: "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, и вы увидите "Hello, World!". оказаны.

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

Rack принимает возвращаемое значение (массив) и использует его для создания экземпляра Rack::Response и отправляет его обратно клиенту. Браузер использует magic для печати 'Hello, World!' на экран.

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

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

Создание приложения стойки взаимодействует с хешем входящей среды

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

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

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

Из документов стойки в среде Hash: "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 в нашу кодовую базу - класс: MessageSetter,
  • хеш среды будет сначала попадать в этот класс и будет передан как параметр: env,
  • MessageSetter будет вставлять ключ 'MESSAGE' в хэш env, его значение 'Hello, World!', если env['QUERY_STRING'] пусто; env['QUERY_STRING'] если нет,
  • наконец, он вернет @app.call(env) - @app следующее приложение в "Stack": 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 docs мы видим, что Rack::Builder реализует небольшую DSL для итеративного построения приложений Rack. Это в основном означает, что вы можете создать "Stack", состоящий из одного или нескольких Middlewares и приложения "нижнего уровня" для отправки. Все запросы, поступающие на ваше приложение нижнего уровня, будут сначала обработаны вашим промежуточным программным обеспечением.

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

Средство промежуточного уровня должно:

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

В нашем случае "промежуточное ПО" MessageSetter, конструктор - это метод MessageSetter initialize, "следующее приложение" в стеке - MessageApp.

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

(перед тем, как продолжить движение),

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

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

Выводы

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

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

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

(над пунктами пули от другого ответа на эту тему)

Вы часто увидите это в профессиональных приложениях Sinatra. Sinatra использует стойку! См. здесь для определения того, что Sinatra 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

Полезные ссылки

Ещё вопросы

Сообщество Overcoder
Наверх
Меню