Штекер это:
Другими словами, Plug позволяет создавать веб-приложения из небольших частей и запускать их на разных веб-серверах. Plug используется веб-платформами, такими как Phoenix, для управления запросами, ответами и веб-сокетами. В этой документации будут показаны некоторые примеры высокого уровня и представлены основные строительные блоки плагина.
Чтобы использовать Plug, вам понадобится веб-сервер и его привязки для Plug. На данный момент есть два варианта:
Используйте веб-сервер Cowboy (на основе Erlang), добавив пакет plug_cowboy
в ваш mix.exs
:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Используйте веб-сервер Bandit (на основе Elixir), добавив bandit
в ваш mix.exs
:
def deps do
[
{ :bandit , "~> 1.0" }
]
end
Это минимальный пример приветствия мира с использованием веб-сервера 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 )
Сохраните этот фрагмент в файл и запустите его как elixir hello_world.exs
. Зайдите на http://localhost:4000/ и вас должны приветствовать!
В примере выше мы написали наш первый модуль plug , который называется MyPlug
. Разъемы модуля должны определять функцию init/1
и функцию call/2
. call/2
вызывается с соединением и параметрами, возвращаемыми init/1
.
Plug v1.14 включает API upgrade
соединения, что означает, что он обеспечивает поддержку WebSocket «из коробки». Давайте посмотрим пример, на этот раз с использованием веб-сервера Bandit и проекта websocket_adapter
для битов WebSocket. Поскольку нам нужны разные маршруты, мы воспользуемся для этого встроенным 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
, который позволяет нам определять маршруты, используемые нашим веб-приложением, и ряд шагов/плагеров, таких как plug Plug.Logger
, которые будут выполняться при каждом запросе.
Более того, как вы можете видеть, Plug абстрагирует различные веб-серверы. При загрузке вашего приложения разница заключается в выборе Plug.Cowboy
или Bandit
.
На данный момент мы запустили сервер напрямую в одноразовом супервизоре, но для производственных развертываний вы хотите запустить его в дереве надзора за приложениями. См. раздел Контролируемые обработчики далее.
В производственной системе вы, вероятно, захотите запустить конвейер Plug под деревом управления вашего приложения. Запустите новый проект Elixir с флагом --sup
:
$ 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
Наконец создайте lib/my_app/my_plug.ex
с модулем MyPlug
.
Теперь запустите mix run --no-halt
, и ваше приложение запустится с веб-сервером, работающим по адресу http://localhost:4001.
Plug.Conn
В примере hello world мы определили наш первый плагин под названием MyPlug
. Существует два типа вилок: модульные вилки и функциональные вилки.
Подключаемый модуль реализует функцию 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, соединение неизменяемо , поэтому каждая манипуляция возвращает новую копию соединения:
conn = put_resp_content_type ( conn , "text/plain" )
conn = send_resp ( conn , 200 , "ok" )
conn
Наконец, имейте в виду, что соединение — это прямой интерфейс к базовому веб-серверу . Когда вы вызываете 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
, представленный локальной (импортированной) функцией match/2
, а затем вызывает плагин :dispatch
, который выполнит соответствующий код.
Plug поставляется со множеством плагинов, которые вы можете добавить в конвейер плагинов маршрутизатора, что позволяет вам подключать что-либо до того, как маршрут будет сопоставлен или до того, как маршрут будет отправлен. Например, если вы хотите добавить ведение журнала на маршрутизатор, просто выполните:
plug Plug.Logger
plug :match
plug :dispatch
Примечание. Plug.Router
компилирует все ваши маршруты в одну функцию и использует виртуальную машину Erlang для оптимизации базовых маршрутов в дереве поиска вместо линейного поиска, который вместо этого будет соответствовать маршруту за маршрутом. Это означает, что поиск маршрутов в Plug!
Это также означает, что рекомендуется определить блок перехвата всех match
, как в примере выше, в противном случае маршрутизация завершится неудачей с ошибкой функционального предложения (как это было бы в любой обычной функции Elixir).
Каждый маршрут должен возвращать соединение согласно спецификации Plug. Дополнительную информацию см. в документации Plug.Router
.
В комплект поставки вилки входит модуль 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
— устанавливает идентификатор запроса, который будет использоваться в журналах;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 г. |
v1.8 | Не поддерживается с 01.2023. |
v1.7 | Не поддерживается с 01.2022. |
v1.6 | Не поддерживается с 01.2022. |
v1.5 | Не поддерживается с 03.2021. |
v1.4 | Не поддерживается с декабря 2018 г. |
v1.3 | Не поддерживается с декабря 2018 г. |
v1.2 | Не поддерживается с 06.2018. |
v1.1 | Не поддерживается с 01.2018. |
v1.0 | Не поддерживается с 05.2017. |
Исходный код плагина распространяется под лицензией Apache License 2.0. Проверьте файл LICENSE для получения дополнительной информации.