La prise est :
En d’autres termes, Plug vous permet de créer des applications Web à partir de petits éléments et de les exécuter sur différents serveurs Web. Plug est utilisé par les frameworks Web tels que Phoenix pour gérer les demandes, les réponses et les websockets. Cette documentation montrera quelques exemples de haut niveau et présentera les principaux éléments de base du Plug.
Pour utiliser Plug, vous avez besoin d'un serveur Web et de ses liaisons pour Plug. Il y a deux options pour le moment :
Utilisez le serveur Web Cowboy (basé sur Erlang) en ajoutant le package plug_cowboy
à votre mix.exs
:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Utilisez le serveur Web Bandit (basé sur Elixir) en ajoutant le package bandit
à votre mix.exs
:
def deps do
[
{ :bandit , "~> 1.0" }
]
end
Ceci est un exemple minimal de Hello World, utilisant le serveur Web Cowboy :
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 )
Enregistrez cet extrait dans un fichier et exécutez-le sous le nom elixir hello_world.exs
. Accédez à http://localhost:4000/ et vous devriez être accueilli !
Dans l'exemple ci-dessus, nous avons écrit notre premier module plug , appelé MyPlug
. Les fiches de module doivent définir la fonction init/1
et la fonction call/2
. call/2
est invoqué avec la connexion et les options renvoyées par init/1
.
Plug v1.14 inclut une API upgrade
de connexion, ce qui signifie qu'il fournit une prise en charge WebSocket prête à l'emploi. Voyons un exemple, utilisant cette fois le serveur Web Bandit et le projet websocket_adapter
pour les bits WebSocket. Puisque nous avons besoin de routes différentes, nous utiliserons le Plug.Router
intégré pour cela :
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 )
Enregistrez cet extrait dans un fichier et exécutez-le sous le nom elixir websockets.exs
. Accédez à http://localhost:4000/ et vous devriez voir des messages dans la console de votre navigateur.
Cette fois, nous avons utilisé Plug.Router
, qui nous permet de définir les routes utilisées par notre application web et une série d'étapes/plugs, comme plug Plug.Logger
, à exécuter à chaque requête.
De plus, comme vous pouvez le constater, Plug résume les différents serveurs Web. Lors du démarrage de votre application, la différence est entre choisir Plug.Cowboy
ou Bandit
.
Pour l'instant, nous avons démarré directement le serveur dans un superviseur jetable mais, pour les déploiements en production, vous souhaitez les démarrer dans l'arborescence de supervision des applications. Consultez ensuite la section Gestionnaires supervisés.
Sur un système de production, vous souhaiterez probablement démarrer votre pipeline Plug sous l'arborescence de supervision de votre application. Démarrez un nouveau projet Elixir avec l'option --sup
:
$ mix new my_app --sup
Ajoutez :plug_cowboy
(ou :bandit
) comme dépendance à votre mix.exs
:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Mettez maintenant à jour lib/my_app/application.ex
comme suit :
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
Créez enfin lib/my_app/my_plug.ex
avec le module MyPlug
.
Exécutez maintenant mix run --no-halt
et votre application démarrera avec un serveur Web exécuté sur http://localhost:4001.
Plug.Conn
Dans l'exemple Hello World, nous avons défini notre premier plug appelé MyPlug
. Il existe deux types de fiches, les fiches de module et les fiches de fonction.
Un plug de module implémente une fonction init/1
pour initialiser les options et une fonction call/2
qui reçoit la connexion et les options initialisées et renvoie la connexion :
defmodule MyPlug do
def init ( [ ] ) , do: false
def call ( conn , _opts ) , do: conn
end
Une fonction plug prend la connexion, un ensemble d'options comme arguments, et renvoie la connexion :
def hello_world_plug ( conn , _opts ) do
conn
|> put_resp_content_type ( "text/plain" )
|> send_resp ( 200 , "Hello world" )
end
Une connexion est représentée par la structure %Plug.Conn{}
:
% Plug.Conn {
host: "www.example.com" ,
path_info: [ "bar" , "baz" ] ,
...
}
Les données peuvent être lues directement à partir de la connexion et également faire correspondre des modèles. La manipulation de la connexion se produit souvent avec l'utilisation des fonctions définies dans le module Plug.Conn
. Dans notre exemple, put_resp_content_type/2
et send_resp/3
sont définis dans Plug.Conn
.
N'oubliez pas que, comme tout le reste dans Elixir, une connexion est immuable , donc chaque manipulation renvoie une nouvelle copie de la connexion :
conn = put_resp_content_type ( conn , "text/plain" )
conn = send_resp ( conn , 200 , "ok" )
conn
Enfin, gardez à l’esprit qu’une connexion est une interface directe avec le serveur Web sous-jacent . Lorsque vous appelez send_resp/3
ci-dessus, il renverra immédiatement le statut et le corps donnés au client. Cela rend les fonctionnalités telles que le streaming un jeu d'enfant.
Plug.Router
Pour écrire un plug "routeur" qui distribue en fonction du chemin et de la méthode des requêtes entrantes, Plug fournit 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
Le routeur est une prise. Ce n’est pas tout : il contient également son propre pipeline de plug-ins. L'exemple ci-dessus indique que lorsque le routeur est invoqué, il invoquera le plug :match
, représenté par une fonction match/2
locale (importée), puis appellera le plug :dispatch
qui exécutera le code correspondant.
Plug est livré avec de nombreux plugs que vous pouvez ajouter au pipeline de plug du routeur, vous permettant de brancher quelque chose avant qu'un itinéraire ne corresponde ou avant qu'un itinéraire ne soit envoyé. Par exemple, si vous souhaitez ajouter la journalisation au routeur, faites simplement :
plug Plug.Logger
plug :match
plug :dispatch
Remarque Plug.Router
compile toutes vos routes en une seule fonction et s'appuie sur la machine virtuelle Erlang pour optimiser les routes sous-jacentes dans une recherche arborescente, au lieu d'une recherche linéaire qui correspondrait à la place route par route. Cela signifie que les recherches d'itinéraires sont extrêmement rapides dans Plug !
Cela signifie également qu'il est recommandé de définir un bloc catch all match
comme dans l'exemple ci-dessus, sinon le routage échoue avec une erreur de clause de fonction (comme ce serait le cas dans n'importe quelle fonction Elixir normale).
Chaque route doit renvoyer la connexion conformément à la spécification Plug. Consultez la documentation Plug.Router
pour plus d’informations.
Plug est livré avec un module Plug.Test
qui facilite le test de vos fiches. Voici comment tester le routeur par le haut (ou toute autre prise) :
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
Ce projet vise à être livré avec différentes fiches pouvant être réutilisées dans toutes les applications :
Plug.BasicAuth
- fournit une authentification HTTP de base ;Plug.CSRFProtection
- ajoute une protection contre la contrefaçon de requêtes intersites à votre application. Généralement requis si vous utilisez Plug.Session
;Plug.Head
- convertit les requêtes HEAD en requêtes GET ;Plug.Logger
- enregistre les requêtes ;Plug.MethodOverride
- remplace une méthode de requête par celle spécifiée dans les paramètres de la requête ;Plug.Parsers
- responsable de l'analyse du corps de la requête en fonction de son type de contenu ;Plug.RequestId
- configure un ID de demande à utiliser dans les journaux ;Plug.RewriteOn
- réécrit l'hôte/le port/le protocole de la requête à partir des en-têtes x-forwarded-*
;Plug.Session
- gère la gestion et le stockage des sessions ;Plug.SSL
- applique les requêtes via SSL ;Plug.Static
- sert des fichiers statiques ;Plug.Telemetry
- instrumente le pipeline de plug avec des événements :telemetry
;Vous pouvez entrer plus en détail sur chacun d’eux dans nos documents.
Modules pouvant être utilisés après avoir utilisé Plug.Router
ou Plug.Builder
pour aider au développement :
Plug.Debugger
- affiche une page de débogage utile à chaque fois qu'il y a un échec dans une requête ;Plug.ErrorHandler
- permet aux développeurs de personnaliser les pages d'erreur en cas de crash au lieu d'en envoyer une vierge ; Nous invitons tout le monde à contribuer à Plug et à nous aider à résoudre les problèmes existants !
Utilisez le suivi des problèmes pour les rapports de bogues ou les demandes de fonctionnalités. Ouvrez une pull request lorsque vous êtes prêt à contribuer. Lorsque vous soumettez une pull request, vous ne devez pas mettre à jour le CHANGELOG.md
.
Si vous envisagez de contribuer à la documentation, veuillez consulter nos meilleures pratiques en matière de rédaction de documentation.
Enfin, n'oubliez pas que toutes les interactions dans nos espaces officiels suivent notre code de conduite.
Bifurquer | Soutien |
---|---|
v1.15 | Corrections de bugs |
v1.14 | Correctifs de sécurité uniquement |
v1.13 | Correctifs de sécurité uniquement |
v1.12 | Correctifs de sécurité uniquement |
v1.11 | Correctifs de sécurité uniquement |
v1.10 | Correctifs de sécurité uniquement |
v1.9 | Non pris en charge à partir de 10/2023 |
v1.8 | Non pris en charge à partir du 01/2023 |
v1.7 | Non pris en charge à partir du 01/2022 |
v1.6 | Non pris en charge à partir du 01/2022 |
v1.5 | Non pris en charge à partir du 03/2021 |
v1.4 | Non pris en charge à partir de 12/2018 |
v1.3 | Non pris en charge à partir de 12/2018 |
v1.2 | Non pris en charge à partir de 06/2018 |
v1.1 | Non pris en charge à partir du 01/2018 |
v1.0 | Non pris en charge à partir de 05/2017 |
Le code source du plug est publié sous licence Apache 2.0. Vérifiez le fichier LICENSE pour plus d’informations.