Stecker ist:
Mit anderen Worten: Mit Plug können Sie Webanwendungen aus kleinen Teilen erstellen und auf verschiedenen Webservern ausführen. Plug wird von Web-Frameworks wie Phoenix zum Verwalten von Anfragen, Antworten und Websockets verwendet. Diese Dokumentation zeigt einige allgemeine Beispiele und stellt die Hauptbausteine des Plugs vor.
Um Plug nutzen zu können, benötigen Sie einen Webserver und dessen Anbindungen für Plug. Im Moment gibt es zwei Möglichkeiten:
Verwenden Sie den Cowboy-Webserver (Erlang-basiert), indem Sie das Paket plug_cowboy
zu Ihrer mix.exs
hinzufügen:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Verwenden Sie den Bandit-Webserver (Elixir-basiert), indem Sie das bandit
-Paket zu Ihrem mix.exs
hinzufügen:
def deps do
[
{ :bandit , "~> 1.0" }
]
end
Dies ist ein minimales Hallo-Welt-Beispiel unter Verwendung des Cowboy-Webservers:
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 )
Speichern Sie dieses Snippet in einer Datei und führen Sie es als elixir hello_world.exs
aus. Greifen Sie auf http://localhost:4000/ zu und Sie sollten begrüßt werden!
Im obigen Beispiel haben wir unser erstes Modul-Plug mit dem Namen MyPlug
geschrieben. Modulstecker müssen die Funktion init/1
und die Funktion call/2
definieren. call/2
wird mit der Verbindung und den von init/1
zurückgegebenen Optionen aufgerufen.
Plug v1.14 enthält eine Verbindungs- upgrade
-API, was bedeutet, dass es sofort WebSocket-Unterstützung bietet. Sehen wir uns ein Beispiel an, dieses Mal mit dem Bandit-Webserver und dem Projekt websocket_adapter
für die WebSocket-Bits. Da wir unterschiedliche Routen benötigen, verwenden wir dafür den eingebauten 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 )
Speichern Sie dieses Snippet in einer Datei und führen Sie es als elixir websockets.exs
aus. Greifen Sie auf http://localhost:4000/ zu und Sie sollten Meldungen in Ihrer Browserkonsole sehen.
Dieses Mal haben wir Plug.Router
verwendet, mit dem wir die von unserer Webanwendung verwendeten Routen und eine Reihe von Schritten/Plugins, wie z. B. plug Plug.Logger
, definieren können, die bei jeder Anfrage ausgeführt werden.
Darüber hinaus abstrahiert Plug, wie Sie sehen, die verschiedenen Webserver. Beim Starten Ihrer Anwendung besteht der Unterschied zwischen der Auswahl von Plug.Cowboy
oder Bandit
.
Im Moment haben wir den Server direkt in einem Einweg-Supervisor gestartet, aber für Produktionsbereitstellungen möchten Sie ihn im Anwendungsüberwachungsbaum starten. Weitere Informationen finden Sie im Abschnitt „Überwachte Handler“.
Auf einem Produktionssystem möchten Sie Ihre Plug-Pipeline wahrscheinlich unter der Überwachungsstruktur Ihrer Anwendung starten. Starten Sie ein neues Elixir-Projekt mit der Flagge --sup
:
$ mix new my_app --sup
Fügen Sie :plug_cowboy
(oder :bandit
) als Abhängigkeit zu Ihrem mix.exs
hinzu:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Aktualisieren Sie nun lib/my_app/application.ex
wie folgt:
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
Erstellen Sie abschließend lib/my_app/my_plug.ex
mit dem MyPlug
-Modul.
Führen Sie nun mix run --no-halt
aus und Ihre Anwendung wird mit einem Webserver gestartet, der unter http://localhost:4001 läuft.
Plug.Conn
Struktur Im Hallo-Welt-Beispiel haben wir unseren ersten Plug namens MyPlug
definiert. Es gibt zwei Arten von Steckern, Modulstecker und Funktionsstecker.
Ein Modul-Plug implementiert eine init/1
Funktion zum Initialisieren der Optionen und eine call/2
-Funktion, die die Verbindung und die initialisierten Optionen empfängt und die Verbindung zurückgibt:
defmodule MyPlug do
def init ( [ ] ) , do: false
def call ( conn , _opts ) , do: conn
end
Ein Funktionsstecker nimmt die Verbindung, eine Reihe von Optionen als Argumente und gibt die Verbindung zurück:
def hello_world_plug ( conn , _opts ) do
conn
|> put_resp_content_type ( "text/plain" )
|> send_resp ( 200 , "Hello world" )
end
Eine Verbindung wird durch die %Plug.Conn{}
-Struktur dargestellt:
% Plug.Conn {
host: "www.example.com" ,
path_info: [ "bar" , "baz" ] ,
...
}
Daten können direkt aus der Verbindung gelesen und auch mit Mustern abgeglichen werden. Die Manipulation der Verbindung erfolgt häufig über die im Plug.Conn
-Modul definierten Funktionen. In unserem Beispiel sind sowohl put_resp_content_type/2
als auch send_resp/3
in Plug.Conn
definiert.
Denken Sie daran, dass eine Verbindung wie alles andere in Elixir unveränderlich ist , sodass jede Manipulation eine neue Kopie der Verbindung zurückgibt:
conn = put_resp_content_type ( conn , "text/plain" )
conn = send_resp ( conn , 200 , "ok" )
conn
Bedenken Sie abschließend, dass eine Verbindung eine direkte Schnittstelle zum zugrunde liegenden Webserver ist. Wenn Sie oben send_resp/3
aufrufen, werden der angegebene Status und der angegebene Text sofort an den Client zurückgesendet. Dadurch ist die Arbeit mit Funktionen wie Streaming ein Kinderspiel.
Plug.Router
Um einen „Router“-Plug zu schreiben, der basierend auf dem Pfad und der Methode eingehender Anforderungen versendet, stellt Plug Plug.Router
bereit:
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
Der Router ist ein Stecker. Darüber hinaus enthält es auch eine eigene Plug-Pipeline. Das obige Beispiel besagt, dass der Router beim Aufruf den :match
Plug aufruft, der durch eine lokale (importierte) match/2
-Funktion dargestellt wird, und dann den :dispatch
Plug aufruft, der den passenden Code ausführt.
Plug wird mit vielen Plugs geliefert, die Sie zur Router-Plug-Pipeline hinzufügen können, sodass Sie etwas anschließen können, bevor eine Route übereinstimmt oder bevor eine Route gesendet wird. Wenn Sie beispielsweise die Protokollierung zum Router hinzufügen möchten, gehen Sie einfach wie folgt vor:
plug Plug.Logger
plug :match
plug :dispatch
Hinweis: Plug.Router
kompiliert alle Ihre Routen in einer einzigen Funktion und verlässt sich auf die Erlang-VM, um die zugrunde liegenden Routen in einer Baumsuche zu optimieren, statt einer linearen Suche, die stattdessen Route für Route abgleichen würde. Dies bedeutet, dass die Routensuche in Plug! extrem schnell ist.
Dies bedeutet auch, dass die Definition eines Catch-All- match
Blocks wie im obigen Beispiel empfohlen wird, andernfalls schlägt das Routing mit einem Funktionsklauselfehler fehl (wie es bei jeder regulären Elixir-Funktion der Fall wäre).
Jede Route muss die Verbindung gemäß der Plug-Spezifikation zurückgeben. Weitere Informationen finden Sie in den Plug.Router
-Dokumenten.
Plug wird mit einem Plug.Test
Modul geliefert, das das Testen Ihrer Stecker erleichtert. So können wir den Router von oben (oder einem anderen Stecker) testen:
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
Dieses Projekt zielt darauf ab, mit verschiedenen Steckern auszuliefern, die anwendungsübergreifend wiederverwendet werden können:
Plug.BasicAuth
– bietet grundlegende HTTP-Authentifizierung;Plug.CSRFProtection
– fügt Ihrer Anwendung Cross-Site Request Forgery-Schutz hinzu. Normalerweise erforderlich, wenn Sie Plug.Session
verwenden.Plug.Head
– konvertiert HEAD-Anfragen in GET-Anfragen;Plug.Logger
– protokolliert Anfragen;Plug.MethodOverride
– überschreibt eine Anforderungsmethode mit einer in den Anforderungsparametern angegebenen;Plug.Parsers
– verantwortlich für das Parsen des Anforderungstexts anhand seines Inhaltstyps;Plug.RequestId
– richtet eine Anforderungs-ID ein, die in Protokollen verwendet werden soll;Plug.RewriteOn
– Host/Port/Protokoll der Anfrage aus x-forwarded-*
Headern neu schreiben;Plug.Session
– kümmert sich um die Sitzungsverwaltung und -speicherung;Plug.SSL
– erzwingt Anfragen über SSL;Plug.Static
– stellt statische Dateien bereit;Plug.Telemetry
– instrumentiert die Plug-Pipeline mit :telemetry
Ereignissen;In unseren Dokumenten können Sie auf die einzelnen Elemente näher eingehen.
Module, die nach der Verwendung Plug.Router
oder Plug.Builder
zur Unterstützung der Entwicklung verwendet werden können:
Plug.Debugger
– zeigt jedes Mal eine hilfreiche Debugging-Seite an, wenn bei einer Anfrage ein Fehler auftritt;Plug.ErrorHandler
– ermöglicht Entwicklern, Fehlerseiten im Falle von Abstürzen anzupassen, anstatt eine leere Seite zu senden; Wir heißen alle herzlich willkommen, zu Plug beizutragen und uns bei der Bewältigung bestehender Probleme zu helfen!
Nutzen Sie den Issue-Tracker für Fehlerberichte oder Funktionsanfragen. Öffnen Sie eine Pull-Anfrage, wenn Sie bereit sind, einen Beitrag zu leisten. Wenn Sie eine Pull-Anfrage senden, sollten Sie CHANGELOG.md
nicht aktualisieren.
Wenn Sie vorhaben, Dokumentation beizutragen, lesen Sie bitte unsere Best Practices zum Schreiben von Dokumentation.
Denken Sie abschließend daran, dass alle Interaktionen in unseren offiziellen Bereichen unserem Verhaltenskodex folgen.
Zweig | Unterstützung |
---|---|
v1.15 | Fehlerbehebungen |
v1.14 | Nur Sicherheitspatches |
v1.13 | Nur Sicherheitspatches |
v1.12 | Nur Sicherheitspatches |
v1.11 | Nur Sicherheitspatches |
v1.10 | Nur Sicherheitspatches |
v1.9 | Ab 10/2023 nicht mehr unterstützt |
v1.8 | Ab 01/2023 nicht mehr unterstützt |
v1.7 | Ab 01/2022 nicht mehr unterstützt |
v1.6 | Ab 01/2022 nicht mehr unterstützt |
v1.5 | Ab 03/2021 nicht mehr unterstützt |
v1.4 | Ab 12/2018 nicht mehr unterstützt |
v1.3 | Ab 12/2018 nicht mehr unterstützt |
v1.2 | Ab 06/2018 nicht mehr unterstützt |
v1.1 | Ab 01/2018 nicht mehr unterstützt |
v1.0 | Ab 05/2017 nicht mehr unterstützt |
Der Plug-Quellcode wird unter der Apache-Lizenz 2.0 veröffentlicht. Weitere Informationen finden Sie in der LICENSE-Datei.