RubyのRackミドルウェアとは何ですか?「ミドルウェア」が何を意味するのかについて、良い説明が見つかりませんでした。
RubyのRackミドルウェアとは何ですか?「ミドルウェア」が何を意味するのかについて、良い説明が見つかりませんでした。
回答:
Rackミドルウェアは、「リクエストとレスポンスをフィルタリングする方法」以上のものです。これは、Rackを使用したWebサーバーのパイプライン設計パターンの実装です。
要求を処理するさまざまな段階を非常に明確に分離します。問題の分離は、適切に設計されたすべてのソフトウェア製品の主要な目標です。
たとえば、Rackを使用すると、パイプラインの個別のステージを実行できます。
認証:リクエストが届いたときに、ユーザーのログオン詳細は正しいですか?このOAuth、HTTP基本認証、名前/パスワードを検証するにはどうすればよいですか?
承認:「ユーザーには、この特定のタスクを実行する権限がありますか?」、つまりロールベースのセキュリティ。
キャッシング:このリクエストはすでに処理しましたが、キャッシュされた結果を返すことはできますか?
装飾:下流の処理を改善するためのリクエストをどのように強化できますか?
パフォーマンスと使用状況の監視:リクエストとレスポンスからどのような統計を取得できますか?
実行:実際に要求を処理し、応答を提供します。
さまざまなステージを分離できること(およびオプションでそれらを含めることができること)は、適切に構造化されたアプリケーションの開発に非常に役立ちます。
また、Rack Middlewareを取り巻く素晴らしいエコシステムが開発されています。事前に構築されたラックコンポーネントを見つけて、上記のすべての手順を実行できるようになるはずです。ミドルウェアのリストについては、Rack GitHub wikiを参照してください。
ミドルウェアは恐ろしい用語であり、何らかのタスクの実行を支援するが、直接関与していないソフトウェアコンポーネント/ライブラリを指します。非常に一般的な例は、ロギング、認証、その他の一般的な水平処理コンポーネントです。これらは、多くの場合、複数のアプリケーションにわたって誰もが必要とするものですが、あまり多くの人々が自分自身の構築に関心を持っている(またはすべきである)ものではありません。
リクエストをフィルタリングする方法であるというコメントは、おそらくRailsCastエピソード151:ラックミドルウェアのスクリーンキャストからのものです。
Rackミドルウェアは、Rackから進化したもので、Introduction to Rackミドルウェアには素晴らしい紹介があります。
ウィキペディアのミドルウェアの紹介がここにあります。
まず最初に、Rackは正確に2つあります。
ラック-Webサーバーインターフェイス
ラックの基本は単純な規則です。すべてのラック準拠のWebサーバーは、指定したオブジェクトのcallメソッドを常に呼び出し、そのメソッドの結果を提供します。Rackは、この呼び出しメソッドがどのように見える必要があるか、また何を返す必要があるかを正確に指定します。それはラックです。
簡単に試してみましょう。私はWEBrickをラック準拠のWebサーバーとして使用しますが、どれでも使用できます。JSON文字列を返す簡単なWebアプリケーションを作成してみましょう。このために、config.ruというファイルを作成します。config.ruは、ラックに準拠したWebサーバーでconfig.ruのコンテンツを実行するだけの、rack gemのコマンドrackupによって自動的に呼び出されます。それでは、config.ruファイルに以下を追加しましょう:
class JSONServer
def call(env)
[200, {"Content-Type" => "application/json"}, ['{ "message" : "Hello!" }']]
end
end
map '/hello.json' do
run JSONServer.new
end
規約に指定されているように、サーバーには環境ハッシュを受け入れ、Webサーバーが提供する[ステータス、ヘッダー、本文]という形式の配列を返す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
urlまたはhttp://localhost:9292/hello.json
voilaをカーリングまたはアクセスして、新しいJSONサーバーをテストしてみましょう。
$ curl http://localhost:9292/hello.json
{ message: "Hello!" }
できます。すごい!RailsであろうとSinatraであろうと、それがすべてのWebフレームワークの基礎です。ある時点で、それらは呼び出しメソッドを実装し、すべてのフレームワークコードを処理し、最後に典型的な[ステータス、ヘッダー、本文]形式で応答を返します。
たとえば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で応答します。したがって、ラックインターフェース規則に準拠しているすべてのWebサーバーは、完全に機能する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はミドルウェアのcallメソッドを自動的に呼び出し、[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
サーバーを再起動して出来上がり。リクエストごとにログを出力します。ラックを使用すると、呼び出される複数のミドルウェアを追加順に追加できます。これは、ラックアプリケーションのコアを変更せずに機能を追加するための優れた方法です。
ラック-宝石
ラック-まず第一に-は慣習ですが、優れた機能を提供する宝石でもあります。JSONサーバーで既に使用しているものの1つであるrackupコマンド。しかし、まだまだあります!ラック宝石は、静的ファイルやディレクトリ全体の提供など、多くのユースケースに対応できる小さなアプリケーションを提供します。htmls / index.htmlにある非常に基本的な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ファイルが完全にレンダリングされていることがわかります。簡単でしたよね?
/ javascriptsの下にいくつかのJavaScriptファイルを作成し、以下をconfig.ruに追加して、JavaScriptファイルのディレクトリ全体を追加してみましょう。
map '/javascripts' do
run Rack::Directory.new "javascripts"
end
サーバーを再起動してアクセスするhttp://localhost:9292/javascript
と、今すぐどこからでも含めることができるすべてのJavaScriptファイルのリストが表示されます。
私は自分のラックをかなりの時間理解するのに問題がありました。このミニチュアRuby Webサーバーを自分で作成するように取り組んだ後、私はそれを完全に理解しました。ここで私のブログ(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
ファミリーメソッドを使用して独自のミドルウェアを追加することもできます。
ミドルウェアに機能を実装する利点は、Railsだけでなく、あらゆるRackフレームワーク、つまりすべての主要なRubyのフレームワークで再利用できることです。
ラックミドルウェアは、アプリケーションに送信される要求と応答をフィルタリングする方法です。ミドルウェアコンポーネントはクライアントとサーバーの間にあり、インバウンド要求とアウトバウンド応答を処理しますが、それはWebサーバーとの通信に使用できるインターフェース以上のものです。通常はRubyクラスであるモジュールをグループ化して順序付けし、モジュール間の依存関係を指定するために使用されます。ラックミドルウェアモジュールは、次の条件のみを満たしている必要があります。–スタック内の次のアプリケーションをパラメーターとして取るコンストラクターがあること–環境呼び出しをパラメーターとして取る「呼び出し」メソッドに応答すること この呼び出しからの戻り値は、ステータスコード、環境ハッシュ、応答本文の配列です。
Rackミドルウェアを使用して、いくつかの問題を解決しました。
どちらの場合もかなりエレガントな修正が行われました。
Rackは、RubyをサポートするWebサーバーとRubyフレームワークの間の最小限のインターフェースを提供します。
Rackを使用すると、Rackアプリケーションを作成できます。
ラックは、環境ハッシュ(クライアントからのHTTPリクエスト内に含まれる、CGIのようなヘッダーで構成されるハッシュ)をラックアプリケーションに渡します。ラックアプリケーションは、このハッシュに含まれるものを使用して、必要なことをすべて実行できます。
Rackを使用するには、「アプリ」を提供する必要があります。これは#call
、環境ハッシュをパラメーターとして使用してメソッドに応答するオブジェクトです(通常はとして定義されますenv
)。#call
正確に3つの値の配列を返す必要があります。
each
)。そのような配列を返すラックアプリケーションを作成できます。これは、レスポンス内でラックによってクライアントに送り返されます(これは実際にはクラスのインスタンスになりRack::Response
ます[ドキュメントに移動するにはクリックしてください])。
gem install rack
config.ru
ファイルを作成する-ラックはこれを探すことを知っています。Rack::Response
レスポンス本文がString:を含む配列であるレスポンス(のインスタンス)を返す小さなラックアプリケーションを作成します"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リクエストをラックに送信し、ラックがインスタンス化MessageApp
して実行call
し、環境ハッシュをパラメーターとしてメソッドに渡します(env
引数)。
Rackは戻り値(配列)を受け取り、それを使用してのインスタンスを作成し、それをRack::Response
クライアントに送り返します。ブラウザはマジックを使用して「Hello、World!」を出力します 画面に。
ちなみに、環境ハッシュがどのように見えるかを確認したい場合は、単にputs env
その下に置いてくださいdef call(env)
。
最小限、ここに書いたのはラックアプリケーションです!
小さなRackアプリでは、env
ハッシュを操作できます(環境ハッシュの詳細については、こちらを参照してください)。
ユーザーが独自のクエリ文字列をURLに入力する機能を実装するため、その文字列はHTTPリクエストに存在し、環境ハッシュのキー/値ペアの1つの値としてカプセル化されます。
私たちのRackアプリはEnvironmentハッシュからそのクエリ文字列にアクセスし、それをレスポンスのBodyを介してクライアント(この場合はブラウザ)に送り返します。
EnvironmentハッシュのRackドキュメントから: 「QUERY_STRING:?に続くリクエストURLの部分(ある場合)。空の場合がありますが、常に必要です!」
#./message_app.rb
class MessageApp
def call(env)
message = env['QUERY_STRING']
[200, {}, [message]]
end
end
次に、(クエリ文字列である)にrackup
アクセスすると、ビューポートにレンダリングされた 'hello'が表示されます。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
ラック:: Builderのドキュメント我々はその参照Rack::Builder
実装繰り返し構造のラックアプリケーションに小さなDSLを。これは基本的に、1つ以上のミドルウェアで構成される「スタック」と、ディスパッチ先の「最下位」アプリケーションを構築できることを意味します。最下位レベルのアプリケーションを通過するすべてのリクエストは、最初にミドルウェアによって処理されます。
#use
スタックで使用するミドルウェアを指定します。引数としてミドルウェアを使用します。
ラックミドルウェアは次の条件を満たす必要があります。
call
環境ハッシュをパラメーターとして受け取るメソッドに応答します。私たちの場合、「ミドルウェア」はMessageSetter
、「コンストラクタ」はMessageSetterのinitialize
メソッド、スタック内の「次のアプリケーション」はMessageApp
です。
したがって、ここでは、内部で何Rack::Builder
が行われるかにより、のメソッドのapp
引数はになります。MessageSetter
initialize
MessageApp
(先に進む前に、上記の周りに頭を向けてください)
したがって、ミドルウェアの各部分は、既存の環境ハッシュをチェーン内の次のアプリケーションに「受け渡し」ます。そのため、スタック内の次のアプリケーションに渡す前に、ミドルウェア内でその環境ハッシュを変更する機会があります。
#run
に応答し#call
、Rack Response(のインスタンスRack::Response
)を返すオブジェクトである引数を取ります。
これを使用Rack::Builder
すると、ミドルウェアのチェーンを構築でき、アプリケーションへのリクエストは各ミドルウェアによって順番に処理されてから、スタックの最後のピース(この場合はMessageApp
)によって最終的に処理されます。これは、リクエストの処理の異なる段階を分離するため、非常に便利です。「懸念の分離」に関しては、それはそれほどクリーンではありません!
次のようなことに対処するいくつかのミドルウェアで構成される「リクエストパイプライン」を構築できます。
(このスレッドの別の回答からの箇条書きの上)
これは、プロのSinatraアプリケーションでよく見られます。シナトラはラックを使用しています!参照してくださいここで何シナトラの定義については、IS!
最後の注意として、私たちconfig.ru
は短縮形で書くことができ、まったく同じ機能を生成します(これが通常表示されるものです)。
require_relative './message_app'
require_relative './middleware/message_setter'
use MessageSetter
run MessageApp.new
そして、何MessageApp
をしているのかをより明確に示すために、必要な3つの引数を使用しての#call
新しいインスタンスを作成していることを明示的に示した「ロングハンド」バージョンRack::Response
を次に示します。
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
ラック-インターフェースb / wウェブとアプリサーバー
Rackは、アプリケーションと通信するためのWebサーバーのインターフェースを提供するRubyパッケージです。Webサーバーとアプリの間にミドルウェアコンポーネントを追加して、リクエスト/レスポンスの動作を変更するのは簡単です。ミドルウェアコンポーネントはクライアントとサーバーの間にあり、インバウンド要求とアウトバウンド応答を処理します。
簡単に言うと、これは基本的に、サーバーとRailsアプリ(またはその他のRuby Webアプリ)が相互に通信する方法に関する一連のガイドラインにすぎません。
Rackを使用するには、「app」を提供します。これは、callメソッドに応答し、環境ハッシュをパラメーターとして受け取り、3つの要素を持つ配列を返すオブジェクトです。
詳細については、以下のリンクをたどってください。
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をラックファイルとして使用しており、rackup
コマンドを使用して任意のラックファイルを実行できます。これのデフォルトのポートは9292
です。これをテストするには、単にrackup
railsディレクトリで実行して結果を確認します。実行するポートを割り当てることもできます。特定のポートでラックファイルを実行するコマンドは
rackup -p PORT_NUMBER
Rackは、HTTPリクエスト/レスポンスを抽象化するシンプルなインターフェースを提供する宝石です。ラックは、アダプターとしてWebフレームワーク(Rails、Sinatraなど)とWebサーバー(ユニコーン、プーマ)の間にあります。上の画像から、これはユニコーンサーバーをレールについて知ることから完全に独立させ、レールはユニコーンについて知らないからです。これは良い例である疎結合、関心の分離。
上の画像は、このhttps://youtu.be/3PnUV9QzB0gでの Railsカンファレンスの講演からのものです。理解を深めるために、この動画を視聴することをおすすめします。