ラックミドルウェアとは何ですか?
-
20-09-2019 - |
質問
RubyのRackミドルウェアとは何ですか?「ミドルウェア」の意味について適切な説明が見つかりませんでした。
解決
デザインとしてのラック
Rack ミドルウェアは「リクエストとレスポンスをフィルタリングする方法」以上のものであり、 パイプライン設計パターン Web サーバーの場合 ラック.
これは、リクエストを処理するさまざまな段階を非常に明確に分離します。懸念事項の分離は、適切に設計されたすべてのソフトウェア製品の重要な目標です。
たとえば、Rack を使用すると、パイプラインの個別のステージで次のことを行うことができます。
認証:リクエストが到着したとき、ユーザーのログオン詳細は正しいですか?この OAuth、HTTP 基本認証、名前/パスワードを検証するにはどうすればよいですか?
認可:「ユーザーはこの特定のタスクを実行する権限を持っていますか?」、つまりロールベースのセキュリティ。
キャッシング:このリクエストはすでに処理しましたが、キャッシュされた結果を返すことはできますか?
装飾:ダウンストリーム処理を改善するためにリクエストを強化するにはどうすればよいですか?
パフォーマンスと使用状況の監視:リクエストとレスポンスからどのような統計を取得できますか?
実行:実際にリクエストを処理し、レスポンスを提供します。
さまざまなステージを分離できる (および必要に応じてステージを含めることができる) ことは、適切に構造化されたアプリケーションを開発する場合に非常に役立ちます。
コミュニティ
また、ラック ミドルウェアを中心に開発されている優れたエコシステムもあります。上記のすべての手順やその他の手順を実行するための、事前に構築されたラック コンポーネントを見つけることができるはずです。見る ミドルウェアのリストについては、Rack GitHub wiki.
ミドルウェアとは何ですか?
ミドルウェアとは、あるタスクの実行を支援するものの、直接関与しないソフトウェア コンポーネント/ライブラリを指す恐ろしい用語です。非常に一般的な例は、ロギング、認証などです。 共通の水平処理コンポーネント. 。これらは、複数のアプリケーションにわたって誰もが必要とするものである傾向がありますが、自分で構築することに興味がある (またはそうすべきである) 人はあまり多くありません。
詳しくは
リクエストをフィルタリングする方法であるというコメントは、おそらく RailsCast エピソード 151:ラックミドルウェア スクリーンキャスト。
Rack ミドルウェアは Rack から進化したもので、次の場所に素晴らしい紹介があります。 Rack ミドルウェアの概要.
ウィキペディアにミドルウェアの概要があります ここ.
他のヒント
まず、Rack には正確に 2 つの要素があります。
- Webサーバーインターフェースの規約
- 宝石
ラック - ウェブサーバー インターフェイス
ラックの基本は単純な規則です。すべてのラック準拠 Web サーバーは、常に、与えられたオブジェクトの call メソッドを呼び出し、そのメソッドの結果を提供します。Rack は、この呼び出しメソッドがどのようなものであるか、また何を返さなければならないかを正確に指定します。それがラックです。
簡単に試してみましょう。ラック準拠の Web サーバーとして WEBrick を使用しますが、どれでも使用できます。JSON 文字列を返す単純な Web アプリケーションを作成してみましょう。このために、config.ru というファイルを作成します。config.ru は、ラック gem のコマンドrackup によって自動的に呼び出され、ラック準拠の Web サーバーで 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
規約により、サーバーには環境ハッシュを受け入れ、Web サーバーが提供する形式 [ステータス、ヘッダー、本文] の配列を返す call と呼ばれるメソッドがあります。単純に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
URL をカールするかアクセスして、新しい JSON サーバーをテストしてみましょう http://localhost:9292/hello.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がこのミドルウェアを使用できるようにするには、次のように 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 を使用すると、追加された順序で呼び出される複数のミドルウェアを追加できます。これは、ラック アプリケーションのコアを変更せずに機能を追加する優れた方法です。
ラック - 宝石
ラックはまず第一に慣例ですが、優れた機能性を備えた逸品でもあります。そのうちの 1 つは、JSON サーバーですでに使用されている、rackup コマンドです。しかし、それだけではありません!ラック gem は、静的ファイルやディレクトリ全体の提供など、多くのユースケースに対応する小さなアプリケーションを提供します。単純なファイル、たとえば htmls/index.html にある非常に基本的な HTML ファイルを提供する方法を見てみましょう。
<!DOCTYPE HTML>
<html>
<head>
<title>The Index</title>
</head>
<body>
<p>Index Page</p>
</body>
</html>
このファイルを Web サイトのルートから提供したい場合があるので、次の内容を 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サーバrel="noreferrer">この 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はそれが機能だたくさんのラックミドルウェアを使用し、あなたがconfig.middleware.use
ファミリー方法であまりにも所有して追加することができます。
ミドルウェアで機能を実装することの利点は、あなただけのRailsの、任意のラックのフレームワーク上でそれを再利用するため、すべての主要なRubyのものとすることができないということです。
ラックミドルウェア、アプリケーションに入ってくる要求と応答をフィルタリングする方法です。ミドルウェアコンポーネントは、インバウンド要求とアウトバウンドの応答を処理し、クライアントとサーバの間に座っているが、それは、Webサーバーとの通信に使用できるインターフェイスよりもです。これは通常のRubyクラスであるグループと順序モジュールに使用され、それらの間の依存関係を指定します。ラックミドルウェアモジュールは、唯一必要があります: - パラメータとして、スタック内の次のアプリケーションを取るコンストラクタを持っている - パラメータとして環境ハッシュを取る方法を、「コール」に応答します。この呼び出しから値を返すことの配列である:ステータスコード、環境ハッシュとレスポンスボディ
Rack ミドルウェアを使用して、いくつかの問題を解決しました。
- カスタム Rack ミドルウェアを使用して JSON 解析エラーをキャッチする クライアントがバストされた JSON を送信すると、適切にフォーマットされたエラー メッセージが返されます。
- Rack::Deflater によるコンテンツ圧縮
どちらの場合も非常にエレガントな修正が行われました。
ラックとは何ですか?
Rack は、Ruby をサポートする Web サーバーと Ruby フレームワークの間に最小限のインターフェイスを提供します。
Rack を使用すると、Rack アプリケーションを作成できます。
Rack は、環境ハッシュ (クライアントからの HTTP リクエスト内に含まれ、CGI のようなヘッダーで構成されるハッシュ) を Rack アプリケーションに渡します。Rack アプリケーションは、このハッシュに含まれるものを使用して必要なことを実行できます。
ラックアプリケーションとは何ですか?
Rack を使用するには、「アプリ」、つまり、 #call
パラメータとして環境ハッシュを使用するメソッド (通常は次のように定義されます) env
). #call
正確に 3 つの値の配列を返す必要があります。
- の ステータスコード (例: '200')、
- ある ヘッダーのハッシュ,
- の 応答本文 (Ruby メソッドに応答する必要があります。
each
).
このような配列を返す Rack アプリケーションを作成できます。これは、Rack によってクライアントに送り返されます。 応答 (これは実際には 実例 クラスの Rack::Response
[クリックしてドキュメントに移動])。
非常にシンプルなラック アプリケーション:
gem install rack
- を作成します
config.ru
ファイル - Rack はこれを探すことを知っています。
レスポンス (のインスタンス) を返す小さなラック アプリケーションを作成します。 Rack::Response
) who の応答本文は文字列を含む配列です。 "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
そして訪問してください ローカルホスト:9292 そして、「Hello, World!」とレンダリングされるはずです。
これは包括的な説明ではありませんが、基本的にここで何が起こっているかというと、クライアント (ブラウザ) がローカル サーバー経由で HTTP リクエストを Rack に送信し、Rack がインスタンスを作成するということです。 MessageApp
そして走ります call
, 、環境ハッシュをパラメーターとしてメソッドに渡します ( env
口論)。
Rack は戻り値 (配列) を受け取り、それを使用して次のインスタンスを作成します。 Rack::Response
そしてそれをクライアントに送り返します。ブラウザが使用するのは、 魔法 「Hello, World!」を画面に出力します。
ちなみに、環境ハッシュがどのようなものかを確認したい場合は、次のように入力します。 puts env
下に def call(env)
.
最小限ですが、ここに書いたのは Rack アプリケーションです。
ラック アプリケーションを受信環境ハッシュと対話させる
私たちの小さな Rack アプリでは、 env
ハッシュ (参照 ここ 環境ハッシュの詳細については、こちらをご覧ください)。
ユーザーが独自のクエリ文字列を URL に入力できる機能を実装します。そのため、その文字列は、環境ハッシュのキーと値のペアの 1 つの値としてカプセル化されて、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 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 アプリケーションを反復的に構築します。これは基本的に、1 つ以上のミドルウェアとディスパッチ先の「最下位」アプリケーションで構成される「スタック」を構築できることを意味します。最下位アプリケーションを通過するすべてのリクエストは、最初にミドルウェアによって処理されます。
#use
スタックで使用するミドルウェアを指定します。ミドルウェアを引数として受け取ります。
ラックミドルウェアは次のことを行う必要があります。
- スタック内の次のアプリケーションをパラメーターとして受け取るコンストラクターがあります。
- に応答する
call
環境ハッシュをパラメータとして受け取るメソッド。
私たちの場合、「ミドルウェア」は MessageSetter
, 、「コンストラクター」は MessageSetter の initialize
メソッドの場合、スタック内の「次のアプリケーション」は MessageApp
.
それで、ここで、何のために Rack::Builder
ボンネットの下で行われます。 app
の議論 MessageSetter
さんの initialize
方法は MessageApp
.
(次に進む前に、上記のことをよく理解してください)
したがって、ミドルウェアの各部分は基本的に、既存の環境ハッシュをチェーン内の次のアプリケーションに「受け渡し」ます。そのため、スタック内の次のアプリケーションに渡す前に、ミドルウェア内でその環境ハッシュを変更する機会があります。
#run
に応答するオブジェクトである引数を受け取ります #call
そして、Rack Response (のインスタンス) を返します。 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
, 、必要な 3 つの引数を指定します。
class MessageApp
def call(env)
Rack::Response.new([env['MESSAGE']], 200, {})
end
end
役立つリンク
Rack - Web およびアプリサーバー間のインターフェース
Rack は、Web サーバーがアプリケーションと通信するためのインターフェースを提供する Ruby パッケージです。Web サーバーとアプリの間にミドルウェア コンポーネントを追加して、リクエスト/レスポンスの動作を変更するのは簡単です。ミドルウェア コンポーネントはクライアントとサーバーの間に位置し、受信リクエストと送信応答を処理します。
平たく言えば、これは基本的に、サーバーと Rails アプリ (またはその他の Ruby Web アプリ) がどのように相互に通信するかについてのガイドラインのセットにすぎません。.
Rack を使用するには、「アプリ」を提供します。call メソッドに応答するオブジェクト。環境ハッシュをパラメータとして受け取り、次の 3 つの要素を含む配列を返します。
- 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 があり、任意のラック ファイルを実行できます。 rackup
指示。そして、このデフォルトのポートは次のとおりです 9292
. 。これをテストするには、次のように実行します。 rackup
Rails ディレクトリに移動して結果を確認します。実行するポートを割り当てることもできます。特定のポートでラック ファイルを実行するコマンドは次のとおりです。
rackup -p PORT_NUMBER