El enchufe es:
En otras palabras, Plug le permite crear aplicaciones web a partir de piezas pequeñas y ejecutarlas en diferentes servidores web. Plug es utilizado por marcos web como Phoenix para gestionar solicitudes, respuestas y websockets. Esta documentación mostrará algunos ejemplos de alto nivel e introducirá los principales componentes básicos del complemento.
Para utilizar Plug, necesita un servidor web y sus enlaces para Plug. Hay dos opciones en este momento:
Utilice el servidor web Cowboy (basado en Erlang) agregando el paquete plug_cowboy
a su mix.exs
:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Utilice el servidor web Bandit (basado en Elixir) agregando el paquete bandit
a su mix.exs
:
def deps do
[
{ :bandit , "~> 1.0" }
]
end
Este es un ejemplo mínimo de Hola mundo, utilizando el servidor 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 )
Guarde ese fragmento en un archivo y ejecútelo como elixir hello_world.exs
. Acceda a http://localhost:4000/ y será recibido.
En el ejemplo anterior, escribimos nuestro primer módulo plug , llamado MyPlug
. Los enchufes del módulo deben definir la función init/1
y la función call/2
. call/2
se invoca con la conexión y las opciones devueltas por init/1
.
Plug v1.14 incluye una API upgrade
de conexión, lo que significa que proporciona compatibilidad con WebSocket lista para usar. Veamos un ejemplo, esta vez usando el servidor web Bandit y el proyecto websocket_adapter
para los bits WebSocket. Como necesitamos diferentes rutas, usaremos el Plug.Router
integrado para eso:
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 )
Guarde ese fragmento en un archivo y ejecútelo como elixir websockets.exs
. Acceda a http://localhost:4000/ y debería ver mensajes en la consola de su navegador.
Esta vez utilizamos Plug.Router
, que nos permite definir las rutas utilizadas por nuestra aplicación web y una serie de pasos/complementos, como plug Plug.Logger
, que se ejecutarán en cada solicitud.
Además, como puede ver, Plug abstrae los diferentes servidores web. Al iniciar su aplicación, la diferencia está entre elegir Plug.Cowboy
o Bandit
.
Por ahora, hemos iniciado el servidor directamente en un supervisor desechable pero, para implementaciones de producción, desea iniciarlos en el árbol de supervisión de aplicaciones. Consulte la sección Manejadores supervisados a continuación.
En un sistema de producción, es probable que desee iniciar su canalización Plug bajo el árbol de supervisión de su aplicación. Inicie un nuevo proyecto de Elixir con la bandera --sup
:
$ mix new my_app --sup
Agregue :plug_cowboy
(o :bandit
) como una dependencia a su mix.exs
:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Ahora actualice lib/my_app/application.ex
de la siguiente manera:
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
Finalmente cree lib/my_app/my_plug.ex
con el módulo MyPlug
.
Ahora ejecute mix run --no-halt
e iniciará su aplicación con un servidor web ejecutándose en http://localhost:4001.
Plug.Conn
En el ejemplo de Hola mundo, definimos nuestro primer complemento llamado MyPlug
. Hay dos tipos de enchufes, enchufes de módulo y enchufes de función.
Un conector de módulo implementa una función init/1
para inicializar las opciones y una función call/2
que recibe la conexión y las opciones inicializadas y devuelve la conexión:
defmodule MyPlug do
def init ( [ ] ) , do: false
def call ( conn , _opts ) , do: conn
end
Un complemento de función toma la conexión, un conjunto de opciones como argumentos y devuelve la conexión:
def hello_world_plug ( conn , _opts ) do
conn
|> put_resp_content_type ( "text/plain" )
|> send_resp ( 200 , "Hello world" )
end
Una conexión está representada por la estructura %Plug.Conn{}
:
% Plug.Conn {
host: "www.example.com" ,
path_info: [ "bar" , "baz" ] ,
...
}
Los datos se pueden leer directamente desde la conexión y también hacer coincidir el patrón. La manipulación de la conexión suele ocurrir con el uso de las funciones definidas en el módulo Plug.Conn
. En nuestro ejemplo, tanto put_resp_content_type/2
como send_resp/3
están definidos en Plug.Conn
.
Recuerda que, como todo en Elixir, una conexión es inmutable , por lo que cada manipulación devuelve una nueva copia de la conexión:
conn = put_resp_content_type ( conn , "text/plain" )
conn = send_resp ( conn , 200 , "ok" )
conn
Finalmente, tenga en cuenta que una conexión es una interfaz directa al servidor web subyacente . Cuando llame send_resp/3
arriba, enviará inmediatamente el estado y el cuerpo dados al cliente. Esto hace que sea muy fácil trabajar con funciones como la transmisión.
Plug.Router
Para escribir un complemento "enrutador" que se envíe según la ruta y el método de las solicitudes entrantes, Plug proporciona 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
El enrutador es un enchufe. No sólo eso: también contiene su propio canal de enchufes. El ejemplo anterior dice que cuando se invoca el enrutador, invocará el complemento :match
, representado por una función match/2
local (importada), y luego llamará al complemento :dispatch
que ejecutará el código coincidente.
Plug se envía con muchos enchufes que puede agregar a la tubería de enchufes del enrutador, lo que le permite enchufar algo antes de que una ruta coincida o antes de que se envíe una ruta. Por ejemplo, si desea agregar registros al enrutador, simplemente haga:
plug Plug.Logger
plug :match
plug :dispatch
Nota Plug.Router
compila todas sus rutas en una sola función y se basa en Erlang VM para optimizar las rutas subyacentes en una búsqueda de árbol, en lugar de una búsqueda lineal que coincidiría ruta por ruta. ¡Esto significa que las búsquedas de rutas son extremadamente rápidas en Plug!
Esto también significa que se recomienda definir un bloque match
como en el ejemplo anterior; de lo contrario, el enrutamiento falla con un error de cláusula de función (como lo haría en cualquier función normal de Elixir).
Cada ruta debe devolver la conexión según la especificación del enchufe. Consulte los documentos Plug.Router
para obtener más información.
El enchufe se envía con un módulo Plug.Test
que facilita la prueba de sus enchufes. Así es como podemos probar el enrutador desde arriba (o cualquier otro enchufe):
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
Este proyecto tiene como objetivo enviar diferentes enchufes que se puedan reutilizar en todas las aplicaciones:
Plug.BasicAuth
: proporciona autenticación HTTP básica;Plug.CSRFProtection
: agrega protección contra falsificación de solicitudes entre sitios a su aplicación. Normalmente es necesario si utiliza Plug.Session
;Plug.Head
: convierte solicitudes HEAD en solicitudes GET;Plug.Logger
: registra solicitudes;Plug.MethodOverride
: anula un método de solicitud con uno especificado en los parámetros de solicitud;Plug.Parsers
: responsable de analizar el cuerpo de la solicitud según su tipo de contenido;Plug.RequestId
: configura un ID de solicitud para utilizarlo en los registros;Plug.RewriteOn
: reescribe el host/puerto/protocolo de la solicitud desde los encabezados x-forwarded-*
;Plug.Session
: maneja la gestión y el almacenamiento de sesiones;Plug.SSL
: aplica solicitudes a través de SSL;Plug.Static
: sirve archivos estáticos;Plug.Telemetry
: instrumenta el canal de conexión con :telemetry
;Puede entrar en más detalles sobre cada uno de ellos en nuestros documentos.
Módulos que se pueden usar después de usar Plug.Router
o Plug.Builder
para ayudar en el desarrollo:
Plug.Debugger
: muestra una página de depuración útil cada vez que hay un error en una solicitud;Plug.ErrorHandler
: permite a los desarrolladores personalizar las páginas de error en caso de fallas en lugar de enviar una en blanco; ¡Invitamos a todos a contribuir a Plug y ayudarnos a abordar los problemas existentes!
Utilice el rastreador de problemas para informes de errores o solicitudes de funciones. Abra una solicitud de extracción cuando esté listo para contribuir. Al enviar una solicitud de extracción, no debe actualizar CHANGELOG.md
.
Si planea contribuir con documentación, consulte nuestras mejores prácticas para redactar documentación.
Finalmente, recuerda que todas las interacciones en nuestros espacios oficiales siguen nuestro Código de Conducta.
Rama | Apoyo |
---|---|
v1.15 | Corrección de errores |
v1.14 | Solo parches de seguridad |
v1.13 | Solo parches de seguridad |
v1.12 | Solo parches de seguridad |
v1.11 | Solo parches de seguridad |
v1.10 | Solo parches de seguridad |
v1.9 | No compatible desde 10/2023 |
v1.8 | No compatible desde 01/2023 |
v1.7 | No compatible desde 01/2022 |
v1.6 | No compatible desde 01/2022 |
v1.5 | No compatible desde 03/2021 |
v1.4 | No compatible desde 12/2018 |
v1.3 | No compatible desde 12/2018 |
v1.2 | No compatible desde 06/2018 |
v1.1 | No compatible desde 01/2018 |
v1.0 | No compatible desde 05/2017 |
El código fuente del complemento se publica bajo la licencia Apache 2.0. Consulte el archivo de LICENCIA para obtener más información.