المكونات هي:
بمعنى آخر، يتيح لك 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
هذا مثال بسيط لـ helloworld، باستخدام خادم الويب 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/ ويجب أن يتم الترحيب بك!
في المثال أعلاه، قمنا بكتابة أول وحدة توصيل لدينا، والتي تسمى MyPlug
. يجب أن تحدد مقابس الوحدة وظيفة init/1
ووظيفة call/2
. يتم استدعاء call/2
مع الاتصال والخيارات التي يتم إرجاعها بواسطة init/1
.
يتضمن Plug v1.14 واجهة برمجة تطبيقات 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
.
في الوقت الحالي، قمنا ببدء تشغيل الخادم مباشرةً في مشرف مهمل، ولكن بالنسبة لعمليات نشر الإنتاج، فأنت تريد تشغيلها في شجرة الإشراف على التطبيق. راجع قسم المعالجات الخاضعة للإشراف التالي.
في نظام الإنتاج، من المحتمل أنك تريد بدء مسار التوصيل الخاص بك تحت شجرة الإشراف الخاصة بالتطبيق الخاص بك. ابدأ مشروع 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
في مثال helloworld، قمنا بتحديد أول مكون لدينا يسمى 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 VM لتحسين المسارات الأساسية في بحث شجرة، بدلاً من البحث الخطي الذي يتطابق بدلاً من ذلك مع المسار لكل مسار. وهذا يعني أن عمليات البحث عن المسار سريعة للغاية في Plug!
هذا يعني أيضًا أنه يوصى بتعريف كتلة match
الكل كما في المثال أعلاه، وإلا فسيفشل التوجيه مع وجود خطأ في جملة الوظيفة (كما يحدث في أي وظيفة Elixir عادية).
يحتاج كل مسار إلى إعادة الاتصال وفقًا لمواصفات التوصيل. راجع مستندات 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 | غير متاح اعتبارًا من 10/2023 |
v1.8 | غير متاح اعتبارًا من 01/2023 |
v1.7 | غير متاح اعتبارًا من 01/2022 |
v1.6 | غير متاح اعتبارًا من 01/2022 |
v1.5 | غير مدعوم اعتبارًا من 03/2021 |
v1.4 | غير مدعوم من 12/2018 |
v1.3 | غير مدعوم من 12/2018 |
v1.2 | غير مدعوم من 06/2018 |
v1.1 | غير مدعوم من 01/2018 |
v1.0 | غير معتمد من 05/2017 |
تم إصدار كود مصدر التوصيل ضمن ترخيص Apache 2.0. تحقق من ملف الترخيص لمزيد من المعلومات.