プラグは:
言い換えれば、Plug を使用すると、小さな部分から Web アプリケーションを構築し、それらを異なる Web サーバーで実行できます。 Plug は、Phoenix などの Web フレームワークによってリクエスト、応答、WebSocket を管理するために使用されます。このドキュメントでは、いくつかの高レベルの例を示し、プラグの主要な構成要素を紹介します。
Plug を使用するには、Web サーバーとその Plug 用のバインディングが必要です。現時点では 2 つのオプションがあります。
plug_cowboy
パッケージをmix.exs
に追加して、Cowboy Web サーバー (Erlang ベース) を使用します。
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
bandit
パッケージをmix.exs
に追加して、Bandit Web サーバー (Elixir ベース) を使用します。
def deps do
[
{ :bandit , "~> 1.0" }
]
end
これは、Cowboy Web サーバーを使用した最小限の Hello World の例です。
Mix . install ( [ :plug , :plug_cowboy ] )
defmodule MyPlug do
import Plug.Conn
def init ( options ) do
# initialize options
options
end
def call ( conn , _opts ) do
conn
|> put_resp_content_type ( "text/plain" )
|> send_resp ( 200 , "Hello world" )
end
end
require Logger
webserver = { Plug.Cowboy , plug: MyPlug , scheme: :http , options: [ port: 4000 ] }
{ :ok , _ } = Supervisor . start_link ( [ webserver ] , strategy: :one_for_one )
Logger . info ( "Plug now running on localhost:4000" )
Process . sleep ( :infinity )
そのスニペットをファイルに保存し、 elixir hello_world.exs
として実行します。 http://localhost:4000/ にアクセスすると、歓迎されるはずです。
上の例では、 MyPlug
という最初のモジュール plugを作成しました。モジュールプラグは、 init/1
関数とcall/2
関数を定義する必要があります。 call/2
、 init/1
によって返された接続とオプションを使用して呼び出されます。
Plug v1.14 には接続upgrade
API が含まれており、すぐに WebSocket サポートを提供します。例を見てみましょう。今回は、Bandit Web サーバーと WebSocket ビットのwebsocket_adapter
プロジェクトを使用します。別のルートが必要なので、そのために組み込みのPlug.Router
を使用します。
Mix . install ( [ :bandit , :websock_adapter ] )
defmodule EchoServer do
def init ( options ) do
{ :ok , options }
end
def handle_in ( { "ping" , [ opcode: :text ] } , state ) do
{ :reply , :ok , { :text , "pong" } , state }
end
def terminate ( :timeout , state ) do
{ :ok , state }
end
end
defmodule Router do
use Plug.Router
plug Plug.Logger
plug :match
plug :dispatch
get "/" do
send_resp ( conn , 200 , """
Use the JavaScript console to interact using websockets
sock = new WebSocket("ws://localhost:4000/websocket")
sock.addEventListener("message", console.log)
sock.addEventListener("open", () => sock.send("ping"))
""" )
end
get "/websocket" do
conn
|> WebSockAdapter . upgrade ( EchoServer , [ ] , timeout: 60_000 )
|> halt ( )
end
match _ do
send_resp ( conn , 404 , "not found" )
end
end
require Logger
webserver = { Bandit , plug: Router , scheme: :http , port: 4000 }
{ :ok , _ } = Supervisor . start_link ( [ webserver ] , strategy: :one_for_one )
Logger . info ( "Plug now running on localhost:4000" )
Process . sleep ( :infinity )
そのスニペットをファイルに保存し、 elixir websockets.exs
として実行します。 http://localhost:4000/ にアクセスすると、ブラウザのコンソールにメッセージが表示されます。
今回は、 Plug.Router
使用しました。これにより、Web アプリケーションで使用されるルートと、リクエストごとに実行されるplug Plug.Logger
などの一連のステップ/プラグを定義できます。
さらに、ご覧のとおり、Plug はさまざまな Web サーバーを抽象化します。アプリケーションを起動するときの違いは、 Plug.Cowboy
選択するかBandit
選択するかです。
現時点では、使い捨てスーパーバイザでサーバーを直接起動していますが、運用環境の場合は、アプリケーション監視ツリーでサーバーを起動する必要があります。次の「監視対象ハンドラー」セクションを参照してください。
実稼働システムでは、アプリケーションの監視ツリーの下で Plug パイプラインを開始することが多いでしょう。 --sup
フラグを使用して新しい Elixir プロジェクトを開始します。
$ mix new my_app --sup
:plug_cowboy
(または:bandit
) を依存関係としてmix.exs
に追加します。
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
次に、 lib/my_app/application.ex
次のように更新します。
defmodule MyApp.Application do
# See https://hexdocs.pm/elixir/Application.html
# for more information on OTP Applications
@ moduledoc false
use Application
def start ( _type , _args ) do
# List all child processes to be supervised
children = [
{ Plug.Cowboy , scheme: :http , plug: MyPlug , options: [ port: 4001 ] }
]
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [ strategy: :one_for_one , name: MyApp.Supervisor ]
Supervisor . start_link ( children , opts )
end
end
最後に、 MyPlug
モジュールを使用してlib/my_app/my_plug.ex
を作成します。
ここでmix run --no-halt
実行すると、http://localhost:4001 で実行されている Web サーバーでアプリケーションが起動されます。
Plug.Conn
構造体hello world の例では、 MyPlug
という最初のプラグを定義しました。プラグにはモジュールプラグとファンクションプラグの 2 種類があります。
モジュール プラグは、オプションを初期化するinit/1
関数と、接続と初期化されたオプションを受け取って接続を返すcall/2
関数を実装します。
defmodule MyPlug do
def init ( [ ] ) , do: false
def call ( conn , _opts ) , do: conn
end
関数プラグは接続、オプションのセットを引数として受け取り、接続を返します。
def hello_world_plug ( conn , _opts ) do
conn
|> put_resp_content_type ( "text/plain" )
|> send_resp ( 200 , "Hello world" )
end
接続は%Plug.Conn{}
構造体で表されます。
% Plug.Conn {
host: "www.example.com" ,
path_info: [ "bar" , "baz" ] ,
...
}
データは接続から直接読み取ることができ、パターン照合も可能です。接続の操作は、多くの場合、 Plug.Conn
モジュールで定義された関数を使用して行われます。この例では、 put_resp_content_type/2
とsend_resp/3
の両方がPlug.Conn
で定義されています。
Elixir の他のすべてと同様、接続は immutable であるため、すべての操作で接続の新しいコピーが返されることに注意してください。
conn = put_resp_content_type ( conn , "text/plain" )
conn = send_resp ( conn , 200 , "ok" )
conn
最後に、接続は基礎となる Web サーバーへの直接インターフェイスであることに留意してください。上記のsend_resp/3
呼び出すと、指定されたステータスと本文がすぐにクライアントに送信されます。これにより、ストリーミングなどの機能が簡単に操作できるようになります。
Plug.Router
受信リクエストのパスとメソッドに基づいてディスパッチする「ルーター」プラグを作成するために、Plug はPlug.Router
提供します。
defmodule MyRouter do
use Plug.Router
plug :match
plug :dispatch
get "/hello" do
send_resp ( conn , 200 , "world" )
end
forward "/users" , to: UsersRouter
match _ do
send_resp ( conn , 404 , "oops" )
end
end
ルーターはプラグです。それだけでなく、独自のプラグ パイプラインも含まれています。上の例では、ルーターが呼び出されるときに、ローカル (インポートされた) match/2
関数で表される:match
プラグを呼び出し、次に、一致したコードを実行する:dispatch
プラグを呼び出すことを示しています。
Plug にはルーターのプラグ パイプラインに追加できる多くのプラグが同梱されており、ルートが一致する前、またはルートがディスパッチされる前に何かをプラグインできます。たとえば、ルーターにログを追加したい場合は、次のようにします。
plug Plug.Logger
plug :match
plug :dispatch
注Plug.Router
すべてのルートを 1 つの関数にコンパイルし、Erlang VM に依存して、ルートごとに一致する線形検索ではなく、基礎となるルートをツリー検索に最適化します。これは、Plug ではルート検索が非常に高速であることを意味します。
これは、上記の例のようにキャッチオールmatch
ブロックを定義することが推奨されることも意味します。定義しないと、(通常の Elixir 関数と同様に) 関数句エラーでルーティングが失敗します。
各ルートはプラグ仕様に従って接続を返す必要があります。詳細については、 Plug.Router
ドキュメントを参照してください。
Plug には、プラグのテストを簡単にするPlug.Test
モジュールが同梱されています。上からルーター (または他のプラグ) をテストする方法は次のとおりです。
defmodule MyPlugTest do
use ExUnit.Case , async: true
use Plug.Test
@ opts MyRouter . init ( [ ] )
test "returns hello world" do
# Create a test connection
conn = conn ( :get , "/hello" )
# Invoke the plug
conn = MyRouter . call ( conn , @ opts )
# Assert the response and status
assert conn . state == :sent
assert conn . status == 200
assert conn . resp_body == "world"
end
end
このプロジェクトは、アプリケーション間で再利用できるさまざまなプラグを同梱して出荷することを目的としています。
Plug.BasicAuth
- 基本的な HTTP 認証を提供します。Plug.CSRFProtection
- アプリケーションにクロスサイト リクエスト フォージェリ保護を追加します。 Plug.Session
使用している場合は通常は必須です。Plug.Head
- HEAD リクエストを GET リクエストに変換します。Plug.Logger
- リクエストをログに記録します。Plug.MethodOverride
- リクエスト メソッドをリクエスト パラメータで指定されたメソッドでオーバーライドします。Plug.Parsers
- 指定されたコンテンツ タイプのリクエスト本文を解析します。Plug.RequestId
- ログで使用されるリクエスト ID を設定します。Plug.RewriteOn
- x-forwarded-*
ヘッダーからリクエストのホスト/ポート/プロトコルを書き換えます。Plug.Session
- セッション管理とストレージを処理します。Plug.SSL
- SSL 経由でリクエストを強制します。Plug.Static
- 静的ファイルを提供します。Plug.Telemetry
- : :telemetry
イベントを使用してプラグ パイプラインを計測します。それぞれの詳細については、ドキュメントを参照してください。
Plug.Router
またはPlug.Builder
を使用した後に開発を支援するために使用できるモジュール:
Plug.Debugger
- リクエストでエラーが発生するたびに、役立つデバッグ ページを表示します。Plug.ErrorHandler
- 開発者は、クラッシュが発生した場合に空のエラー ページを送信する代わりにエラー ページをカスタマイズできます。 Plug に貢献し、既存の問題への取り組みにご協力いただける皆様を歓迎します。
バグレポートや機能リクエストには問題トラッカーを使用してください。貢献する準備ができたら、プル リクエストを開きます。プル リクエストを送信するときは、 CHANGELOG.md
更新しないでください。
ドキュメントに貢献する予定がある場合は、ドキュメント作成のベスト プラクティスを確認してください。
最後に、公式スペースでのすべてのやり取りは行動規範に従っていることを忘れないでください。
支店 | サポート |
---|---|
v1.15 | バグ修正 |
v1.14 | セキュリティパッチのみ |
v1.13 | セキュリティパッチのみ |
v1.12 | セキュリティパッチのみ |
v1.11 | セキュリティパッチのみ |
v1.10 | セキュリティパッチのみ |
v1.9 | 2023 年 10 月以降はサポートされなくなります |
v1.8 | 2023 年 1 月以降はサポートされなくなります |
v1.7 | 2022 年 1 月以降サポートされなくなります |
v1.6 | 2022 年 1 月以降サポートされなくなります |
v1.5 | 2021 年 3 月以降はサポートされなくなります |
v1.4 | 2018 年 12 月以降サポートされなくなりました |
v1.3 | 2018 年 12 月以降サポートされなくなりました |
v1.2 | 2018 年 6 月以降サポートされなくなりました |
v1.1 | 2018 年 1 月以降サポートされなくなりました |
v1.0 | 2017 年 5 月以降サポートされなくなりました |
プラグのソース コードは Apache License 2.0 に基づいてリリースされています。詳細については、LICENSE ファイルを確認してください。