Stekernya adalah:
Dengan kata lain, Plug memungkinkan Anda membangun aplikasi web dari bagian kecil dan menjalankannya di server web yang berbeda. Plug digunakan oleh kerangka web seperti Phoenix untuk mengelola permintaan, respons, dan soket web. Dokumentasi ini akan menunjukkan beberapa contoh tingkat tinggi dan memperkenalkan elemen penyusun utama Plug.
Untuk menggunakan Plug, Anda memerlukan server web dan pengikatannya untuk Plug. Ada dua opsi saat ini:
Gunakan server web Cowboy (berbasis Erlang) dengan menambahkan paket plug_cowboy
ke mix.exs
Anda :
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Gunakan server web Bandit (berbasis Elixir) dengan menambahkan paket bandit
ke mix.exs
Anda :
def deps do
[
{ :bandit , "~> 1.0" }
]
end
Ini adalah contoh minimal hello world, menggunakan server 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 )
Simpan cuplikan itu ke file dan jalankan sebagai elixir hello_world.exs
. Akses http://localhost:4000/ dan Anda akan disambut!
Pada contoh di atas, kami menulis modul plug pertama kami, yang disebut MyPlug
. Steker modul harus mendefinisikan fungsi init/1
dan fungsi call/2
. call/2
dipanggil dengan koneksi dan opsi dikembalikan oleh init/1
.
Plug v1.14 menyertakan API upgrade
koneksi, yang berarti ia menyediakan dukungan WebSocket secara langsung. Mari kita lihat contohnya, kali ini menggunakan server web Bandit dan proyek websocket_adapter
untuk bit WebSocket. Karena kita memerlukan rute yang berbeda, kita akan menggunakan Plug.Router
bawaan untuk itu:
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 )
Simpan cuplikan itu ke file dan jalankan sebagai elixir websockets.exs
. Akses http://localhost:4000/ dan Anda akan melihat pesan di konsol browser Anda.
Kali ini, kita menggunakan Plug.Router
, yang memungkinkan kita menentukan rute yang digunakan oleh aplikasi web kita dan serangkaian langkah/plug, seperti plug Plug.Logger
, untuk dieksekusi pada setiap permintaan.
Selanjutnya, seperti yang Anda lihat, Plug mengabstraksi server web yang berbeda. Saat mem-boot aplikasi Anda, perbedaannya adalah antara memilih Plug.Cowboy
atau Bandit
.
Untuk saat ini, kami telah memulai server secara langsung di supervisor sekali pakai, tetapi untuk penerapan produksi, Anda ingin memulainya di pohon pengawasan aplikasi. Lihat bagian Pengendali yang diawasi selanjutnya.
Pada sistem produksi, Anda mungkin ingin memulai pipeline Plug di bawah pohon pengawasan aplikasi Anda. Mulai proyek Elixir baru dengan flag --sup
:
$ mix new my_app --sup
Tambahkan :plug_cowboy
(atau :bandit
) sebagai ketergantungan pada mix.exs
Anda:
def deps do
[
{ :plug_cowboy , "~> 2.0" }
]
end
Sekarang perbarui lib/my_app/application.ex
sebagai berikut:
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
Terakhir buat lib/my_app/my_plug.ex
dengan modul MyPlug
.
Sekarang jalankan mix run --no-halt
dan itu akan memulai aplikasi Anda dengan server web yang berjalan di http://localhost:4001.
Plug.Conn
Dalam contoh hello world, kami mendefinisikan plug pertama kami yang disebut MyPlug
. Ada dua jenis colokan, colokan modul dan colokan fungsi.
Steker modul mengimplementasikan fungsi init/1
untuk menginisialisasi opsi dan fungsi call/2
yang menerima koneksi dan menginisialisasi opsi dan mengembalikan koneksi:
defmodule MyPlug do
def init ( [ ] ) , do: false
def call ( conn , _opts ) , do: conn
end
Steker fungsi mengambil koneksi, serangkaian opsi sebagai argumen, dan mengembalikan koneksi:
def hello_world_plug ( conn , _opts ) do
conn
|> put_resp_content_type ( "text/plain" )
|> send_resp ( 200 , "Hello world" )
end
Koneksi diwakili oleh struct %Plug.Conn{}
:
% Plug.Conn {
host: "www.example.com" ,
path_info: [ "bar" , "baz" ] ,
...
}
Data dapat dibaca langsung dari koneksi dan juga pencocokan pola. Memanipulasi koneksi sering kali terjadi dengan menggunakan fungsi yang ditentukan dalam modul Plug.Conn
. Dalam contoh kita, put_resp_content_type/2
dan send_resp/3
didefinisikan di Plug.Conn
.
Ingatlah bahwa, seperti semua hal lainnya di Elixir, koneksi tidak dapat diubah , jadi setiap manipulasi akan mengembalikan salinan koneksi yang baru:
conn = put_resp_content_type ( conn , "text/plain" )
conn = send_resp ( conn , 200 , "ok" )
conn
Terakhir, perlu diingat bahwa koneksi adalah antarmuka langsung ke server web yang mendasarinya . Saat Anda memanggil send_resp/3
di atas, status dan isi yang diberikan akan segera dikirim kembali ke klien. Hal ini membuat fitur seperti streaming menjadi mudah untuk digunakan.
Plug.Router
Untuk menulis plug "router" yang dikirimkan berdasarkan jalur dan metode permintaan masuk, Plug menyediakan 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
Router adalah sebuah colokan. Tidak hanya itu: ia juga berisi pipa stekernya sendiri. Contoh di atas mengatakan bahwa ketika router dipanggil, ia akan memanggil :match
plug, yang diwakili oleh fungsi match/2
lokal (yang diimpor), dan kemudian memanggil :dispatch
plug yang akan mengeksekusi kode yang cocok.
Pasang kapal dengan banyak colokan yang dapat Anda tambahkan ke pipa colokan router, memungkinkan Anda untuk menyambungkan sesuatu sebelum rute cocok atau sebelum rute dikirim. Misalnya, jika Anda ingin menambahkan logging ke router, lakukan saja:
plug Plug.Logger
plug :match
plug :dispatch
Catatan Plug.Router
mengkompilasi semua rute Anda ke dalam satu fungsi dan mengandalkan Erlang VM untuk mengoptimalkan rute yang mendasarinya ke dalam pencarian pohon, bukan pencarian linier yang akan mencocokkan rute per rute. Ini berarti pencarian rute sangat cepat di Plug!
Ini juga berarti bahwa blok catch all match
direkomendasikan untuk didefinisikan seperti pada contoh di atas, jika tidak, perutean akan gagal dengan kesalahan klausa fungsi (seperti yang terjadi pada fungsi Elixir biasa).
Setiap rute harus mengembalikan koneksi sesuai spesifikasi Plug. Lihat dokumen Plug.Router
untuk informasi lebih lanjut.
Steker dikirimkan dengan modul Plug.Test
yang memudahkan pengujian colokan Anda. Inilah cara kami menguji router dari atas (atau konektor lainnya):
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
Proyek ini bertujuan untuk dikirimkan dengan colokan berbeda yang dapat digunakan kembali di berbagai aplikasi:
Plug.BasicAuth
- menyediakan otentikasi HTTP Dasar;Plug.CSRFProtection
- menambahkan perlindungan Pemalsuan Permintaan Lintas Situs ke aplikasi Anda. Biasanya diperlukan jika Anda menggunakan Plug.Session
;Plug.Head
- mengubah permintaan HEAD menjadi permintaan GET;Plug.Logger
- mencatat permintaan;Plug.MethodOverride
- mengganti metode permintaan dengan metode yang ditentukan dalam parameter permintaan;Plug.Parsers
- bertanggung jawab untuk mengurai isi permintaan berdasarkan tipe kontennya;Plug.RequestId
- menyiapkan ID permintaan untuk digunakan dalam log;Plug.RewriteOn
- menulis ulang host/port/protokol permintaan dari header x-forwarded-*
;Plug.Session
- menangani manajemen dan penyimpanan sesi;Plug.SSL
- menerapkan permintaan melalui SSL;Plug.Static
- menyajikan file statis;Plug.Telemetry
- menginstrumentasikan pipa plug dengan :telemetry
;Anda dapat mengetahui detail lebih lanjut tentang masing-masingnya di dokumen kami.
Modul yang dapat digunakan setelah Anda menggunakan Plug.Router
atau Plug.Builder
untuk membantu pengembangan:
Plug.Debugger
- menampilkan halaman debugging yang berguna setiap kali ada kegagalan dalam permintaan;Plug.ErrorHandler
- memungkinkan pengembang untuk menyesuaikan halaman kesalahan jika terjadi kerusakan alih-alih mengirimkan halaman kosong; Kami menyambut semua orang untuk berkontribusi pada Plug dan membantu kami mengatasi masalah yang ada!
Gunakan pelacak masalah untuk laporan bug atau permintaan fitur. Buka permintaan tarik ketika Anda siap berkontribusi. Saat mengirimkan permintaan tarik, Anda tidak boleh memperbarui CHANGELOG.md
.
Jika Anda berencana untuk menyumbangkan dokumentasi, silakan periksa praktik terbaik kami untuk menulis dokumentasi.
Terakhir, ingatlah bahwa semua interaksi di ruang resmi kita mematuhi Kode Etik kita.
Cabang | Mendukung |
---|---|
v1.15 | Perbaikan bug |
v1.14 | Patch keamanan saja |
v1.13 | Patch keamanan saja |
v1.12 | Patch keamanan saja |
v1.11 | Patch keamanan saja |
v1.10 | Patch keamanan saja |
v1.9 | Tidak didukung mulai 10/2023 |
v1.8 | Tidak didukung mulai 01/2023 |
v1.7 | Tidak didukung mulai 01/2022 |
v1.6 | Tidak didukung mulai 01/2022 |
v1.5 | Tidak didukung mulai 03/2021 |
v1.4 | Tidak didukung mulai 12/2018 |
v1.3 | Tidak didukung mulai 12/2018 |
v1.2 | Tidak didukung mulai 06/2018 |
v1.1 | Tidak didukung mulai 01/2018 |
v1.0 | Tidak didukung mulai 05/2017 |
Kode sumber plug dirilis di bawah Lisensi Apache 2.0. Periksa file LISENSI untuk informasi lebih lanjut.