Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Compose web applications with functions

License

NotificationsYou must be signed in to change notification settings

elixir-plug/plug

Repository files navigation

Build Statushex.pmhexdocs.pm

Plug is:

  1. A specification for composing web applications with functions
  2. Connection adapters for different web servers in the Erlang VM

In other words, Plug allows you to build web applications from small pieces and run them on different web servers. Plug is used by web frameworks such asPhoenix to manage requests, responses, and websockets. This documentation will show some high-level examples and introduce the Plug's main building blocks.

Installation

In order to use Plug, you need a webserver and its bindings for Plug.There are two options at the moment:

  1. Use the Cowboy webserver (Erlang-based) by adding theplug_cowboy package to yourmix.exs:

    defdepsdo[{:plug_cowboy,"~> 2.0"}]end
  2. Use the Bandit webserver (Elixir-based) by adding thebandit package to yourmix.exs:

    defdepsdo[{:bandit,"~> 1.0"}]end

Hello world: request/response

This is a minimal hello world example, using the Cowboy webserver:

Mix.install([:plug,:plug_cowboy])defmoduleMyPlugdoimportPlug.Conndefinit(options)do# initialize optionsoptionsenddefcall(conn,_opts)doconn|>put_resp_content_type("text/plain")|>send_resp(200,"Hello world")endendrequireLoggerwebserver={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)

Save that snippet to a file and execute it aselixir hello_world.exs.Accesshttp://localhost:4000/ and you should be greeted!

In the example above, we wrote our firstmodule plug, calledMyPlug.Module plugs must define theinit/1 function and thecall/2 function.call/2 is invoked with the connection and the options returned byinit/1.

Hello world: websockets

Plug v1.14 includes a connectionupgrade API, which means it provides WebSocketsupport out of the box. Let's see an example, this time using the Bandit webserverand thewebsocket_adapter project for the WebSocket bits. Since we need differentroutes, we will use the built-inPlug.Router for that:

Mix.install([:bandit,:websock_adapter])defmoduleEchoServerdodefinit(options)do{:ok,options}enddefhandle_in({"ping",[opcode::text]},state)do{:reply,:ok,{:text,"pong"},state}enddefterminate(:timeout,state)do{:ok,state}endenddefmoduleRouterdousePlug.RouterplugPlug.Loggerplug:matchplug:dispatchget"/"dosend_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"))    """)endget"/websocket"doconn|>WebSockAdapter.upgrade(EchoServer,[],timeout:60_000)|>halt()endmatch_dosend_resp(conn,404,"not found")endendrequireLoggerwebserver={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)

Save that snippet to a file and execute it aselixir websockets.exs.Accesshttp://localhost:4000/ and you should see messages in your browserconsole.

This time, we usedPlug.Router, which allows us to define the routesused by our web application and a series of steps/plugs, such asplug Plug.Logger, to be executed on every request.

Furthermore, as you can see, Plug abstracts the different webservers.When booting up your application, the difference is between choosingPlug.Cowboy orBandit.

For now, we have directly started the server in a throw-away supervisor but,for production deployments, you want to start them in applicationsupervision tree. See theSupervised handlers section next.

Supervised handlers

On a production system, you likely want to start your Plug pipeline under your application's supervision tree. Start a new Elixir project with the--sup flag:

$ mix new my_app --sup

Add:plug_cowboy (or:bandit) as a dependency to yourmix.exs:

defdepsdo[{:plug_cowboy,"~> 2.0"}]end

Now updatelib/my_app/application.ex as follows:

defmoduleMyApp.Applicationdo# See https://hexdocs.pm/elixir/Application.html# for more information on OTP Applications@moduledocfalseuseApplicationdefstart(_type,_args)do# List all child processes to be supervisedchildren=[{Plug.Cowboy,scheme::http,plug:MyPlug,options:[port:4001]}]# See https://hexdocs.pm/elixir/Supervisor.html# for other strategies and supported optionsopts=[strategy::one_for_one,name:MyApp.Supervisor]Supervisor.start_link(children,opts)endend

Finally createlib/my_app/my_plug.ex with theMyPlug module.

Now runmix run --no-halt and it will start your application with a web server running athttp://localhost:4001.

Plugs and thePlug.Conn struct

In the hello world example, we defined our first plug calledMyPlug. There are two types of plugs, module plugs and function plugs.

A module plug implements aninit/1 function to initialize the options and acall/2 function which receives the connection and initialized options and returns the connection:

defmoduleMyPlugdodefinit([]),do:falsedefcall(conn,_opts),do:connend

A function plug takes the connection, a set of options as arguments, and returns the connection:

defhello_world_plug(conn,_opts)doconn|>put_resp_content_type("text/plain")|>send_resp(200,"Hello world")end

A connection is represented by the%Plug.Conn{} struct:

%Plug.Conn{host:"www.example.com",path_info:["bar","baz"],  ...}

Data can be read directly from the connection and also pattern matched on. Manipulating the connection often happens with the use of the functions defined in thePlug.Conn module. In our example, bothput_resp_content_type/2 andsend_resp/3 are defined inPlug.Conn.

Remember that, as everything else in Elixir,a connection is immutable, so every manipulation returns a new copy of the connection:

conn=put_resp_content_type(conn,"text/plain")conn=send_resp(conn,200,"ok")conn

Finally, keep in mind that a connection is adirect interface to the underlying web server. When you callsend_resp/3 above, it will immediately send the given status and body back to the client. This makes features like streaming a breeze to work with.

Plug.Router

To write a "router" plug that dispatches based on the path and method of incoming requests, Plug providesPlug.Router:

defmoduleMyRouterdousePlug.Routerplug:matchplug:dispatchget"/hello"dosend_resp(conn,200,"world")endforward"/users",to:UsersRoutermatch_dosend_resp(conn,404,"oops")endend

The router is a plug. Not only that: it contains its own plug pipeline too. The example above says that when the router is invoked, it will invoke the:match plug, represented by a local (imported)match/2 function, and then call the:dispatch plug which will execute the matched code.

Plug ships with many plugs that you can add to the router plug pipeline, allowing you to plug something before a route matches or before a route is dispatched to. For example, if you want to add logging to the router, just do:

plugPlug.Loggerplug:matchplug:dispatch

NotePlug.Router compiles all of your routes into a single function and relies on the Erlang VM to optimize the underlying routes into a tree lookup, instead of a linear lookup that would instead match route-per-route. This means route lookups are extremely fast in Plug!

This also means that a catch allmatch block is recommended to be defined as in the example above, otherwise routing fails with a function clause error (as it would in any regular Elixir function).

Each route needs to return the connection as per the Plug specification. See thePlug.Router docs for more information.

Testing plugs

Plug ships with aPlug.Test module that makes testing your plugs easy. Here is how we can test the router from above (or any other plug):

defmoduleMyPlugTestdouseExUnit.Case,async:trueimportPlug.TestimportPlug.Conn@optsMyRouter.init([])test"returns hello world"do# Create a test connectionconn=conn(:get,"/hello")# Invoke the plugconn=MyRouter.call(conn,@opts)# Assert the response and statusassertconn.state==:sentassertconn.status==200assertconn.resp_body=="world"endend

Available plugs

This project aims to ship with different plugs that can be re-used across applications:

  • Plug.BasicAuth - provides Basic HTTP authentication;
  • Plug.CSRFProtection - adds Cross-Site Request Forgery protection to your application. Typically required if you are usingPlug.Session;
  • Plug.Head - converts HEAD requests to GET requests;
  • Plug.Logger - logs requests;
  • Plug.MethodOverride - overrides a request method with one specified in the request parameters;
  • Plug.Parsers - responsible for parsing the request body given its content-type;
  • Plug.RequestId - sets up a request ID to be used in logs;
  • Plug.RewriteOn - rewrite the request's host/port/protocol fromx-forwarded-* headers;
  • Plug.Session - handles session management and storage;
  • Plug.SSL - enforces requests through SSL;
  • Plug.Static - serves static files;
  • Plug.Telemetry - instruments the plug pipeline with:telemetry events;

You can go into more details about each of themin our docs.

Helper modules

Modules that can be used after you usePlug.Router orPlug.Builder to help development:

  • Plug.Debugger - shows a helpful debugging page every time there is a failure in a request;
  • Plug.ErrorHandler - allows developers to customize error pages in case of crashes instead of sending a blank one;

Contributing

We welcome everyone to contribute to Plug and help us tackle existing issues!

Use theissue tracker for bug reports or feature requests. Open apull request when you are ready to contribute. When submitting a pull request you should not update theCHANGELOG.md.

If you are planning to contribute documentation,please check our best practices for writing documentation.

Finally, remember all interactions in our official spaces follow ourCode of Conduct.

Supported Versions

BranchSupport
v1.15Bug fixes
v1.14Security patches only
v1.13Security patches only
v1.12Security patches only
v1.11Security patches only
v1.10Security patches only
v1.9Unsupported from 10/2023
v1.8Unsupported from 01/2023
v1.7Unsupported from 01/2022
v1.6Unsupported from 01/2022
v1.5Unsupported from 03/2021
v1.4Unsupported from 12/2018
v1.3Unsupported from 12/2018
v1.2Unsupported from 06/2018
v1.1Unsupported from 01/2018
v1.0Unsupported from 05/2017

License

Plug source code is released under Apache License 2.0.Check LICENSE file for more information.

About

Compose web applications with functions

Resources

License

Stars

Watchers

Forks


[8]ページ先頭

©2009-2025 Movatter.jp