Oxygen عبارة عن عمل فني صغير مبني على مكتبة HTTP.JL. التنفس سهلة مع العلم أنه يمكنك تدوير خادم ويب بسرعة مع التجريدات التي تعرفها بالفعل.
هل تحتاج إلى مساعدة؟ لا تتردد في التواصل مع قنوات التواصل الاجتماعي الخاصة بنا.
pkg > add Oxygenقم بإنشاء خادم ويب مع أسطر قليلة جدًا من التعليمات البرمجية
using Oxygen
using HTTP
@get " /greet " function (req :: HTTP.Request )
return " hello world! "
end
# start the web server
serve ()يتم استخدام المعالجات لتوصيل الكود الخاص بك بالخادم بطريقة نظيفة ومباشرة. يقومون بتعيين عنوان URL لوظيفة ما ويستدعيون الوظيفة عندما يتطابق طلب وارد لهذا عنوان URL.
do..end ببناء بناء الجملةRequest افتراضيًا عند عدم توفير معلومات النوعهناك 3 أنواع من المعالجات المدعومة:
Request معالجاتStreamWebsocket using HTTP
using Oxygen
# Request Handler
@get " / " function (req :: HTTP.Request )
...
end
# Stream Handler
@stream " /stream " function (stream :: HTTP.Stream )
...
end
# Websocket Handler
@websocket " /ws " function (ws :: HTTP.WebSocket )
...
end إنها مجرد وظائف مما يعني أن هناك العديد من الطرق التي يمكن التعبير عنها وتحديدها. فيما يلي مثال على عدة طرق مختلفة يمكنك التعبير عنها وتعيين معالج Request .
@get " /greet " function ()
" hello world! "
end
@get ( " /gruessen " ) do
" Hallo Welt! "
end
@get " /saluer " () -> begin
" Bonjour le monde! "
end
@get " /saludar " () -> " ¡Hola Mundo! "
@get " /salutare " f () = " ciao mondo! "
# This function can be declared in another module
function subtract (req, a :: Float64 , b :: Float64 )
return a - b
end
# register foreign request handlers like this
@get " /subtract/{a}/{b} " subtract يتم استخدام معالجات الطلب للتعامل مع طلبات HTTP. يتم تعريفها باستخدام وحدات الماكرو أو مكافئات وظائفها ، وقبول كائن HTTP.Request كوسيطة أولى. هذه المعالجات تدعم كل من الوظيفة وبناء الجملة.
@get ، @post ، @put ، @patch ، @delete ، @routeget() ، post() ، put() ، patch() ، delete() ، route() يتم استخدام معالجات الدفق لدفق البيانات. يتم تعريفها باستخدام Macro @stream أو دالة stream() وقبول كائن HTTP.Stream كوسيطة أولى. هذه المعالجات تدعم كل من الوظيفة وبناء الجملة.
@stream و stream() تعريف نوع على الوسيطة الأولى ، يفترضون أنه دفق.Stream باستخدام وحدات الماكرو والوظائف القياسية: @get ، @post ، إلخStream يتم استخدام معالجات WebSocket للتعامل مع اتصالات WebSocket. يتم تعريفها باستخدام @websocket Macro أو وظيفة websocket() وقبول كائن HTTP.WebSocket كوسيطة أولى. هذه المعالجات تدعم كل من الوظيفة وبناء الجملة.
@websocket و websocket() تعريفًا نوعًا على الوسيطة الأولى ، فهي تفترض أنه websocket.Websocket مع وظيفة Macro @get أو get() ، لأن بروتوكول WebSocket يتطلب طلب GET الحصول على المصافحة.Websocket هناك طريقتان أساسيتان لتسجيل معالجات الطلب الخاصة بك: وحدات الماكرو القياسية للتوجيه أو وظائف التوجيه التي تستخدم بناء جملة DO-Block.
لكل ماكرو توجيه ، لدينا الآن وظيفة توجيه مكافئة
@get -> get ()
@post -> post ()
@put -> put ()
@patch -> patch ()
@delete -> delete ()
@route -> route ()الفرق العملي الوحيد بين الاثنين هو أن وحدات الماكرو التوجيهية تسمى خلال مرحلة التحميل المسبق ، في حين يتم استدعاء وظائف التوجيه فقط عند الاحتجاج. (تدعو وحدات الماكرو التوجيهية وظائف التوجيه أسفل الغطاء)
# Routing Macro syntax
@get " /add/{x}/{y} " function (request :: HTTP.Request , x :: Int , y :: Int )
x + y
end
# Routing Function syntax
get ( " /add/{x}/{y} " ) do request :: HTTP.Request , x :: Int , y :: Int
x + y
end يحدد الأكسجين ، افتراضيًا ، تلقائيًا نوع المحتوى لقيمة الإرجاع من معالج الطلب عند إنشاء استجابة. هذه الوظيفة الافتراضية مفيدة للغاية ، ولكن لها تأثير على الأداء. في المواقف التي يُعرف فيها نوع الإرجاع ، يوصى باستخدام إحدى وظائف العرض الموجودة مسبقًا لتسريع الأمور.
فيما يلي قائمة بوظائف العرض المدعومة حاليًا: html ، text ، json ، file ، xml ، js ، css ، binary
فيما يلي مثال على كيفية استخدام هذه الوظائف:
using Oxygen
get ( " /html " ) do
html ( " <h1>Hello World</h1> " )
end
get ( " /text " ) do
text ( " Hello World " )
end
get ( " /json " ) do
json ( Dict ( " message " => " Hello World " ))
end
serve () في معظم الحالات ، تقبل هذه الوظائف السلاسل العادية كمدخلات. الاستثناءات الوحيدة هي الوظيفة binary ، التي تقبل Vector{UInt8} ، ودالة json التي تقبل أي نوع قابل للتسلسل.
يتم الإعلان عن معلمات المسار بأقواس ويتم نقلها مباشرة إلى معالج الطلب الخاص بك.
using Oxygen
# use path params without type definitions (defaults to Strings)
@get " /add/{a}/{b} " function (req, a, b)
return parse (Float64, a) + parse (Float64, b)
end
# use path params with type definitions (they are automatically converted)
@get " /multiply/{a}/{b} " function (req, a :: Float64 , b :: Float64 )
return a * b
end
# The order of the parameters doesn't matter (just the name matters)
@get " /subtract/{a}/{b} " function (req, b :: Int64 , a :: Int64 )
return a - b
end
# start the web server
serve ()يمكن الإعلان عن معلمات الاستعلام مباشرة داخل توقيع معالجاتك. يُفترض أن أي معلمة لم يتم ذكرها داخل مسار الطريق هي معلمة استعلام.
@get " /query " function (req :: HTTP.Request , a :: Int , message :: String = " hello world " )
return (a, message)
end بدلاً من ذلك ، يمكنك استخدام وظيفة queryparams() لاستخراج القيم الخام من عنوان URL كقاموس.
@get " /query " function (req :: HTTP.Request )
return queryparams (req)
end استخدم وظيفة formdata() لاستخراج وتجفيف بيانات النموذج من جسم الطلب. هذه الوظيفة تُرجع قاموسًا لأزواج القيمة الرئيسية من النموذج
using Oxygen
# Setup a basic form
@get " / " function ()
html ( """
<form action="/form" method="post">
<label for="firstname">First name:</label><br>
<input type="text" id="firstname" name="firstname"><br>
<label for="lastname">Last name:</label><br>
<input type="text" id="lastname" name="lastname"><br><br>
<input type="submit" value="Submit">
</form>
""" )
end
# Parse the form data and return it
@post " /form " function (req)
data = formdata (req)
return data
end
serve ()يتم تلقائي جميع الكائنات تلقائيًا في JSON باستخدام مكتبة JSON3
using Oxygen
using HTTP
@get " /data " function (req :: HTTP.Request )
return Dict ( " message " => " hello! " , " value " => 99.3 )
end
# start the web server
serve ()يوفر Oxygen بعض التسلسل التسلسلي والتخليص عن معظم الكائنات ولكنه يتطلب استخدام الأنواع المتبقية عند تحويل الهياكل
using Oxygen
using HTTP
using StructTypes
struct Animal
id :: Int
type :: String
name :: String
end
# Add a supporting struct type definition so JSON3 can serialize & deserialize automatically
StructTypes . StructType ( :: Type{Animal} ) = StructTypes . Struct ()
@get " /get " function (req :: HTTP.Request )
# serialize struct into JSON automatically (because we used StructTypes)
return Animal ( 1 , " cat " , " whiskers " )
end
@post " /echo " function (req :: HTTP.Request )
# deserialize JSON from the request body into an Animal struct
animal = json (req, Animal)
# serialize struct back into JSON automatically (because we used StructTypes)
return animal
end
# start the web server
serve ()يأتي Oxygen مع العديد من المستخلصات المدمجة المصممة لتقليل كمية الغلاية المطلوبة لتسلسل المدخلات إلى وظائف معالجك. من خلال تحديد بنية وتحديد مصدر البيانات ، تقوم هذه المستخرجات بتبسيط عملية ابتلاع البيانات والتحقق من صحة من خلال واجهة برمجة تطبيقات موحدة.
payload@kwdefالمستخرجون المدعومون:
Path - مقتطفات من معلمات المسارQuery - مقتطفات من معلمات الاستعلام ،Header - مقتطفات من رؤوس الطلبForm - يستخلص بيانات النماذج من هيئة الطلبBody - تسلسل الجسم بالكامل إلى نوع معين (سلسلة ، Float64 ، إلخ ..)ProtoBuffer - يستخرج رسالة ProtoBuf من هيئة الطلب (متوفرة من خلال امتداد الحزمة)Json - يستخرج JSON من هيئة الطلبJsonFragment - يستخرج "جزء" من جسم JSON باستخدام اسم المعلمة لتحديد واستخراج مفتاح المستوى الأعلى المقابل في هذا المثال ، نظهر أنه يمكن استخدام مستخرج Path إلى جانب معلمات المسار العادية. هذا يعمل أيضًا مع معلمات الاستعلام العادية ومستخرج Query .
struct Add
b :: Int
c :: Int
end
@get " /add/{a}/{b}/{c} " function (req, a :: Int , pathparams :: Path{Add} )
add = pathparams . payload # access the serialized payload
return a + add . b + add . c
end يمكن إعداد القيم الافتراضية باستخدام الهياكل باستخدام Macro @kwdef .
@kwdef struct Pet
name :: String
age :: Int = 10
end
@post " /pet " function (req, params :: Json{Pet} )
return params . payload # access the serialized payload
end علاوة على تسلسل البيانات الواردة ، يمكنك أيضًا تحديد قواعد التحقق الخاصة بك باستخدام وظيفة validate . في المثال أدناه ، نوضح كيفية استخدام كل من المدققين global local في الكود الخاص بك.
global قبل تشغيل مدقق local . import Oxygen : validate
struct Person
name :: String
age :: Int
end
# Define a global validator
validate (p :: Person ) = p . age >= 0
# Only the global validator is ran here
@post " /person " function (req, newperson :: Json{Person} )
return newperson . payload
end
# In this case, both global and local validators are ran (this also makes sure the person is age 21+)
# You can also use this sytnax instead: Json(Person, p -> p.age >= 21)
@post " /adult " function (req, newperson = Json {Person} (p -> p . age >= 21 ))
return newperson . payload
end يمكنك الاستعانة بالمتغيرات مباشرة في المسارات ، مما يجعل مسجلات مسجلة ديناميكيًا نسيمًا
(بفضل Anandijain على الفكرة)
using Oxygen
operations = Dict ( " add " => + , " multiply " => * )
for (pathname, operator) in operations
@get " / $pathname /{a}/{b} " function (req, a :: Float64 , b :: Float64 )
return operator (a, b)
end
end
# start the web server
serve () دالة router() هي HOF (وظيفة ترتيب أعلى) تتيح لك إعادة استخدام نفس وخصائص المسار عبر نقاط نهاية متعددة. هذا مفيد عندما تبدأ واجهة برمجة التطبيقات في النمو وتريد أن تبقي عمليات المسار الخاصة بك منظمة.
فيما يلي الحجج التي يمكن أن تأخذها وظيفة router() :
router (prefix :: String ; tags :: Vector , middleware :: Vector , interval :: Real , cron :: String )tags - تستخدم لتنظيم نقاط النهاية في المستندات المولدة تلقائيًاmiddleware - يستخدم لإعداد جهاز التوجيه والوسيط الخاص بالمسارinterval - يستخدم لدعم إجراءات التكرار ( استدعاء معالج الطلب على فاصل محدد في ثوان )cron - لتحديد تعبير CRON الذي يحدد وقت استدعاء معالج الطلب. using Oxygen
# Any routes that use this router will be automatically grouped
# under the 'math' tag in the autogenerated documenation
math = router ( " /math " , tags = [ " math " ])
# You can also assign route specific tags
@get math ( " /multiply/{a}/{b} " , tags = [ " multiplication " ]) function (req, a :: Float64 , b :: Float64 )
return a * b
end
@get math ( " /divide/{a}/{b} " ) function (req, a :: Float64 , b :: Float64 )
return a / b
end
serve ()يأتي Oxygen مع نظام جدولة CRON مدمج يتيح لك استدعاء نقاط النهاية والوظائف تلقائيًا عندما يتطابق تعبير CRON مع الوقت الحالي.
عند جدولة وظيفة ، يتم إنشاء مهمة جديدة وتشغيلها في الخلفية. تستخدم كل مهمة تعبير CRON المعطى والوقت الحالي لتحديد المدة التي يحتاجها للنوم قبل تنفيذها.
يعتمد محلل Cron في الأكسجين على نفس المواصفات التي تستخدم في الربيع. يمكنك العثور على مزيد من المعلومات حول هذا على صفحة Spring Cron Expressions.
فيما يلي انهيار لما تمثله كل معلمة في تعبير CRON الخاص بنا. على الرغم من أن مواصفاتنا تشبه الوثيقة التي تحددها الربيع ، فهي ليست مباراة دقيقة من 1 إلى 1.
The string has six single space-separated time and date fields:
┌───────────── second (0-59)
│ ┌───────────── minute (0 - 59)
│ │ ┌───────────── hour (0 - 23)
│ │ │ ┌───────────── day of the month (1 - 31)
│ │ │ │ ┌───────────── month (1 - 12) (or JAN-DEC)
│ │ │ │ │ ┌───────────── day of the week (1 - 7)
│ │ │ │ │ │ (Monday is 1, Tue is 2... and Sunday is 7)
│ │ │ │ │ │
* * * * * *
يتم دعم التعبيرات الجزئية أيضًا ، مما يعني أنه يمكن استبعاد التعبيرات اللاحقة (يتم التخلف عن سداد '*' ).
# In this example we see only the `seconds` part of the expression is defined.
# This means that all following expressions are automatically defaulted to '*' expressions
@cron " */2 " function ()
println ( " runs every 2 seconds " )
end تحتوي وظيفة router() على وسيطة للكلمة الرئيسية تسمى cron ، والتي تقبل تعبير CRON الذي يحدد متى يتم استدعاء نقطة النهاية. تمامًا مثل وسيطات الكلمات الرئيسية الأخرى ، يمكن إعادة استخدامها بواسطة نقاط النهاية التي تشترك في أجهزة التوجيه أو يتم تجاوزها بنقاط النهاية الموروثة.
# execute at 8, 9 and 10 o'clock of every day.
@get router ( " /cron-example " , cron = " 0 0 8-10 * * * " ) function (req)
println ( " here " )
end
# execute this endpoint every 5 seconds (whenever current_seconds % 5 == 0)
every5 = router ( " /cron " , cron = " */5 " )
# this endpoint inherits the cron expression
@get every5 ( " /first " ) function (req)
println ( " first " )
end
# Now this endpoint executes every 2 seconds ( whenever current_seconds % 2 == 0 ) instead of every 5
@get every5 ( " /second " , cron = " */2 " ) function (req)
println ( " second " )
end بالإضافة إلى جدولة نقاط النهاية ، يمكنك أيضًا استخدام Macro @cron الجديد لجدولة الوظائف. هذا مفيد إذا كنت ترغب في تشغيل رمز في أوقات محددة دون جعلها مرئية أو قابلة للاتصال في API.
@cron " */2 " function ()
println ( " runs every 2 seconds " )
end
@cron " 0 0/30 8-10 * * * " function ()
println ( " runs at 8:00, 8:30, 9:00, 9:30, 10:00 and 10:30 every day " )
end عندما تقوم بتشغيل serve() أو serveparallel() ، يتم تشغيل جميع وظائف CRON المسجلة تلقائيًا. إذا تم إيقاف الخادم أو قتله ، فسيتم أيضًا إنهاء جميع الوظائف الجارية. يمكنك إيقاف الخادم وجميع المهام المتكررة ووظائف CRON عن طريق استدعاء وظيفة terminate() أو قتل الخادم يدويًا باستخدام ctrl+C .
بالإضافة إلى ذلك ، يوفر Oxygen وظائف فائدة لبدء وظائف Cron ووقفها يدويًا: startcronjobs() و stopcronjobs() . يمكن استخدام هذه الوظائف خارج خادم الويب أيضًا.
توفر المهام المتكررة واجهة برمجة تطبيقات بسيطة لتشغيل وظيفة على فاصل محدد.
هناك طريقتان لتسجيل المهام المتكررة:
interval في router()@repeat repeat من المهم أن نلاحظ أن معالجات الطلب التي تستخدم هذه الخاصية لا يمكنها تحديد معلمات الوظائف الإضافية خارج معلمة HTTP.Request الافتراضية.
في المثال أدناه ، تسمى نقطة النهاية /repeat/hello كل 0.5 ثانية ويتم طباعة "hello" إلى وحدة التحكم في كل مرة.
تحتوي وظيفة router() على معلمة interval يتم استخدامها للاتصال بوجه طلب على فاصل محدد (بالثواني).
using Oxygen
taskrouter = router ( " /repeat " , interval = 0.5 , tags = [ " repeat " ])
@get taskrouter ( " /hello " ) function ()
println ( " hello " )
end
# you can override properties by setting route specific values
@get taskrouter ( " /bonjour " , interval = 1.5 ) function ()
println ( " bonjour " )
end
serve ()فيما يلي مثال على كيفية تسجيل مهمة تكرار خارج جهاز التوجيه
@repeat 1.5 function ()
println ( " runs every 1.5 seconds " )
end
# you can also "name" a repeat task
@repeat 5 " every-five " function ()
println ( " runs every 5 seconds " )
end عندما يتم تشغيل الخادم ، يتم بدء جميع المهام تلقائيًا. لكن الوحدة النمطية توفر أيضًا الأدوات المساعدة للحصول على مزيد من التحكم الدقيق في مهام التشغيل باستخدام الوظائف التالية: starttasks() ، stoptasks() ، و cleartasks()
يمكن للأكسجين الاندماج مع المراجعة لتوفير عمليات إعادة تحميل ساخنة ، وسرعة التطوير. نظرًا لأن Revise توصي بإبقاء جميع التعليمات البرمجية ليتم مراجعتها في حزمة ، فأنت تحتاج أولاً إلى الانتقال إلى هذا النوع من التصميم.
تأكد أولاً من أن Project.toml لديه الحقول المطلوبة مثل name للعمل على حزمة بدلاً من مشروع.
بعد ذلك ، اكتب الكود الرئيسي لك في وحدة src/MyModule.jl :
module MyModule
using Oxygen; @oxidise
@get "/greet" function(req::HTTP.Request)
return "hello world!"
end
end
ثم يمكنك عمل نص debug.jl Pointpoint:
using Revise
using Oxygen
using MyModule
MyModule.serve(revise=:eager)
يمكن أيضًا ضبط خيار revise على :lazy ، وفي هذه الحالة ، سيتم ترك المراجعات دائمًا قبل تقديم الطلب ، بدلاً من محاولة شغف عندما تتغير ملفات المصدر على القرص.
لاحظ أنه يجب عليك تشغيل برنامج نصي آخر للدخول دون مراجعة في الإنتاج.
في بعض السيناريوهات المتقدمة ، قد تحتاج إلى تدوير سفرات ويب متعددة داخل نفس الوحدة النمطية على منافذ مختلفة. يوفر Oxygen طريقة ثابتة وديناميكية لإنشاء مثيلات متعددة لخادم الويب.
كقاعدة عامة من الإبهام ، إذا كنت تعرف عدد الحالات التي تحتاجها في وقت مبكر ، فمن الأفضل أن تتعامل مع النهج الثابت.
@oxidiseيوفر Oxygen ماكرو جديد مما يجعل من الممكن إعداد وتشغيل مثيلات متعددة. يولد طرقًا ويربطها بحالة داخلية جديدة للوحدة الحالية.
في المثال أدناه ، يتم تعريف خادمان بسيطان ضمن الوحدات النمطية A و B وتبدأ في الوحدة الأصل. تحتوي كلتا الوحدات على جميع الوظائف التي تم تصديرها من الأكسجين والتي يمكن تسميتها مباشرة كما هو موضح أدناه.
module A
using Oxygen; @oxidise
get ( " / " ) do
text ( " server A " )
end
end
module B
using Oxygen; @oxidise
get ( " / " ) do
text ( " server B " )
end
end
try
# start both instances
A . serve (port = 8001 , async = true )
B . serve (port = 8002 , async = false )
finally
# shut down if we `Ctrl+C`
A . terminate ()
B . terminate ()
endinstance() تساعدك وظيفة instance على إنشاء مثيل مستقل تمامًا لخادم ويب Oxygen في وقت التشغيل. إنه يعمل عن طريق إنشاء وحدة Julia ديناميكيًا في وقت التشغيل وتحميل رمز الأكسجين داخله.
جميع الطرق نفسها من Oxygen متوفرة تحت المثيل المسماة. في المثال أدناه ، يمكننا استخدام get ، serve بمجرد استخدام SYTAX DOT على متغير app1 للوصول إلى الطرق الأساسية.
using Oxygen
# ######## Setup the first app #########
app1 = instance ()
app1 . get ( " / " ) do
text ( " server A " )
end
# ######## Setup the second app #########
app2 = instance ()
app2 . get ( " / " ) do
text ( " server B " )
end
# ######## Start both instances #########
try
# start both servers together
app1 . serve (port = 8001 , async = true )
app2 . serve (port = 8002 )
finally
# clean it up
app1 . terminate ()
app2 . terminate ()
end بالنسبة للسيناريوهات التي تحتاج فيها إلى التعامل مع كميات أعلى من حركة المرور ، يمكنك تشغيل الأكسجين في وضع متعدد التربعات. من أجل الاستفادة من هذا الوضع ، يجب أن يكون لدى جوليا أكثر من مؤشر ترابط واحد للعمل معه. يمكنك بدء جلسة جوليا مع 4 مؤشرات ترابط باستخدام الأمر أدناه
julia --threads 4 serveparallel() يبدأ خادم الويب في وضع البث ويتولى الطلبات في نهج تعدد المهام التعاونية. تستخدم هذه الوظيفة Threads.@spawn لجدولة مهمة جديدة على أي مؤشر ترابط متاح. وفي الوقت نفسه ، يتم استخدام ASYNC داخل هذه المهمة عند الاتصال بكل معالج طلب. هذا يسمح للمهمة بالعائد خلال عمليات الإدخال/الإخراج.
using Oxygen
using StructTypes
using Base . Threads
# Make the Atomic struct serializable
StructTypes . StructType ( :: Type{Atomic{Int64}} ) = StructTypes . Struct ()
x = Atomic {Int64} ( 0 );
@get " /show " function ()
return x
end
@get " /increment " function ()
atomic_add! (x, 1 )
return x
end
# start the web server in parallel mode
serveparallel () يتضمن Oxygen امتدادًا لحزمة ProtoBuf.JL. يوفر هذا الامتداد وظيفة protobuf() ، مما يؤدي إلى تبسيط عملية العمل مع مخازن المؤسسات البروتوكول في سياق خادم الويب. لفهم أفضل لهذه الحزمة ، يرجى الرجوع إلى وثائقها الرسمية.
تحتوي هذه الوظيفة على أحمال زائدة للسيناريوهات التالية:
using HTTP
using ProtoBuf
using Oxygen
# The generated classes need to be created ahead of time (check the protobufs)
include ( " people_pb.jl " );
using . people_pb : People, Person
# Decode a Protocol Buffer Message
@post " /count " function (req :: HTTP.Request )
# decode the request body into a People object
message = protobuf (req, People)
# count the number of Person objects
return length (message . people)
end
# Encode & Return Protocol Buffer message
@get " /get " function ()
message = People ([
Person ( " John Doe " , 20 ),
Person ( " Alice " , 30 ),
Person ( " Bob " , 35 )
])
# seralize the object inside the body of a HTTP.Response
return protobuf (message)
endفيما يلي مثال على المخطط الذي تم استخدامه لإنشاء روابط جوليا اللازمة. تسمح هذه الروابط بترميز الرسائل وفك تشفيرها في المثال أعلاه.
syntax = "proto3" ;
message Person {
string name = 1 ;
sint32 age = 2 ;
}
message People {
repeated Person people = 1 ;
} تم تجهيز Oxygen بالعديد من ملحقات الحزم التي تعزز قدرات التآمر. هذه الامتدادات تجعل من السهل إرجاع المؤامرات مباشرة من معالجات الطلب. يتم تنفيذ جميع العمليات في الذاكرة باستخدام iobuffer وإرجاع HTTP.Response
الحزم المدعومة ومساعدهم:
png ، svg ، pdf ، htmlhtmlhtml using CairoMakie : heatmap
using Oxygen
@get " /cairo " function ()
fig, ax, pl = heatmap ( rand ( 50 , 50 ))
png (fig)
end
serve () using Bonito
using WGLMakie : heatmap
using Oxygen
using Oxygen : html # Bonito also exports html
@get " /wgl " function ()
fig = heatmap ( rand ( 50 , 50 ))
html (fig)
end
serve () using Bonito
using WGLMakie : heatmap
using Oxygen
using Oxygen : html # Bonito also exports html
@get " /bonito " function ()
app = App () do
return DOM . div (
DOM . h1 ( " Random 50x50 Heatmap " ),
DOM . div ( heatmap ( rand ( 50 , 50 )))
)
end
return html (app)
end
serve () بدلاً من بناء محرك داخلي لالتقاط أو إضافة تبعيات إضافية ، يوفر Oxygen امتدادات حزمة لدعم قوالب Mustache.jl و OteraEngine.jl .
يوفر Oxygen واجهة برمجة تطبيقات بسيطة حول كلتا الحزمتين مما يجعل من السهل تقديم قوالب من السلاسل والقوالب والملفات. تُرجع واجهة render تطبيقات الغلاف هذه وظيفة عرض تقبل قاموس المدخلات لملء القالب.
في جميع السيناريوهات ، يتم إرجاع القالب المقدم داخل كائن http.response جاهز للخدمة من قبل واجهة برمجة التطبيقات. بشكل افتراضي ، يتم اكتشاف أنواع MIME تلقائيًا إما عن طريق النظر إلى محتوى القالب أو اسم التمديد في الملف. إذا كنت تعرف نوع MIME ، فيمكنك تمريره مباشرة من خلال وسيطة الكلمات الرئيسية mime_type لتخطي عملية الكشف.
يرجى إلقاء نظرة على وثائق Matache.jl لمعرفة القدرات الكاملة للحزمة
مثال 1: تقديم قالب شارب من ملف
using Mustache
using Oxygen
# Load the Mustache template from a file and create a render function
render = mustache ( " ./templates/greeting.txt " , from_file = false )
@get " /mustache/file " function ()
data = Dict ( " name " => " Chris " )
return render (data) # This will return an HTML.Response with the rendered template
endمثال 2: تحديد نوع MIME لقالب شارب السلسلة العادي
using Mustache
using Oxygen
# Define a Mustache template (both plain strings and mustache templates are supported)
template_str = " Hello, {{name}}! "
# Create a render function, specifying the MIME type as text/plain
render = mustache (template_str, mime_type = " text/plain " ) # mime_type keyword arg is optional
@get " /plain/text " function ()
data = Dict ( " name " => " Chris " )
return render (data) # This will return a plain text response with the rendered template
endيرجى إلقاء نظرة على وثائق oteraengine.jl لتعلم القدرات الكاملة للحزمة
مثال 1: تقديم قالب Otera بالمنطق والحلقات
using OteraEngine
using Oxygen
# Define an Otera template
template_str = """
<html>
<head><title>{{ title }}</title></head>
<body>
{% for name in names %}
Hello {{ name }}<br>
{% end %}
</body>
</html>
"""
# Create a render function for the Otera template
render = otera (template_str)
@get " /otera/loop " function ()
data = Dict ( " title " => " Greetings " , " names " => [ " Alice " , " Bob " , " Chris " ])
return render (data) # This will return an HTML.Response with the rendered template
endفي هذا المثال ، يتم تعريف قالب OTERA مع حلقة من أجل تكرار قائمة الأسماء ، تحية كل اسم.
مثال 2: تشغيل كود جوليا في قالب أوتيرا
using OteraEngine
using Oxygen
# Define an Otera template with embedded Julia code
template_str = """
The square of {{ number }} is {< number^2 >}.
"""
# Create a render function for the Otera template
render = otera (template_str)
@get " /otera/square " function ()
data = Dict ( " number " => 5 )
return render (data) # This will return an HTML.Response with the rendered template
end
في هذا المثال ، يتم تعريف قالب Otera مع رمز Julia المضمن الذي يحسب مربع رقم معين.
يمكنك تركيب الملفات الثابتة باستخدام هذه الوظيفة المفيدة التي تبحث بشكل متكرر في مجلد للملفات وتثبيت كل شيء. يتم تحميل جميع الملفات في الذاكرة عند بدء التشغيل.
using Oxygen
# mount all files inside the "content" folder under the "/static" path
staticfiles ( " content " , " static " )
# start the web server
serve ()على غرار StaticFiles ، تقوم هذه الوظيفة بتركيب كل مسار وإعادة قراءة الملف لكل طلب. هذا يعني أنه سيتم عرض أي تغييرات على الملفات بعد بدء الخادم.
using Oxygen
# mount all files inside the "content" folder under the "/dynamic" path
dynamicfiles ( " content " , " dynamic " )
# start the web server
serve () يمكن أن يوفر تعطيل المسجل الداخلي بعض مكاسب الأداء الضخمة ، والتي يمكن أن تكون مفيدة في بعض السيناريوهات. قصصية ، لقد رأيت تسريع 2-3x في serve() و 4-5x تسريع في الأداء serveparallel() .
# This is how you disable internal logging in both modes
serve (access_log = nothing )
serveparallel (access_log = nothing ) يوفر Oxygen تنسيق تسجيل افتراضي ولكنه يسمح لك بتخصيص التنسيق باستخدام معلمة access_log . هذه الوظيفة متوفرة في كل من serve() والوظائف serveparallel() .
يمكنك قراءة المزيد حول خيارات التسجيل هنا
# Uses the default logging format
serve ()
# Customize the logging format
serve (access_log = logfmt " [$time_iso8601] " $request " $status " )
# Disable internal request logging
serve (access_log = nothing )تعمل وظائف الوسيطة على سهولة إنشاء مهام سير عمل مخصصة لاعتراض جميع الطلبات الواردة والاستجابات الصادرة. يتم تنفيذها في نفس الترتيب الذي يتم فيه تمريرهم (من اليسار إلى اليمين).
يمكن تعيينها في التطبيق ، جهاز التوجيه ، وطبقة المسار مع وسيطة الكلمات الرئيسية middleware . جميع البرامج الوسيطة مضافة وسيتم دمج أي برامج وسيطة محددة في هذه الطبقات وتنفيذها.
سيتم تنفيذ البرامج الوسيطة دائمًا بالترتيب التالي:
application -> router -> route
الآن دعنا نرى بعض البرامج الوسيطة في العمل:
using Oxygen
using HTTP
const CORS_HEADERS = [
" Access-Control-Allow-Origin " => " * " ,
" Access-Control-Allow-Headers " => " * " ,
" Access-Control-Allow-Methods " => " POST, GET, OPTIONS "
]
# https://juliaweb.github.io/HTTP.jl/stable/examples/#Cors-Server
function CorsMiddleware (handler)
return function (req :: HTTP.Request )
println ( " CORS middleware " )
# determine if this is a pre-flight request from the browser
if HTTP . method (req) == " OPTIONS "
return HTTP . Response ( 200 , CORS_HEADERS)
else
return handler (req) # passes the request to the AuthMiddleware
end
end
end
function AuthMiddleware (handler)
return function (req :: HTTP.Request )
println ( " Auth middleware " )
# ** NOT an actual security check ** #
if ! HTTP . headercontains (req, " Authorization " , " true " )
return HTTP . Response ( 403 )
else
return handler (req) # passes the request to your application
end
end
end
function middleware1 (handle)
function (req)
println ( " middleware1 " )
handle (req)
end
end
function middleware2 (handle)
function (req)
println ( " middleware2 " )
handle (req)
end
end
# set middleware at the router level
math = router ( " math " , middleware = [middleware1])
# set middleware at the route level
@get math ( " /divide/{a}/{b} " , middleware = [middleware2]) function (req, a :: Float64 , b :: Float64 )
return a / b
end
# set application level middleware
serve (middleware = [CorsMiddleware, AuthMiddleware])إذا كنت لا ترغب في استخدام مسلسل الاستجابة الافتراضية لـ Oxygen ، فيمكنك إيقاف تشغيله وإضافته! ما عليك سوى إنشاء وظيفة الوسيطة الخاصة الخاصة بك لتسلسل الاستجابة وإضافتها في نهاية سلسلة الوسيطة الخاصة بك.
كلاهما serve() و serveparallel() لديه وسيطة الكلمات الرئيسية serialize والتي يمكن أن تبديل التسلسل الافتراضي.
using Oxygen
using HTTP
using JSON3
@get " /divide/{a}/{b} " function (req :: HTTP.Request , a :: Float64 , b :: Float64 )
return a / b
end
# This is just a regular middleware function
function myserializer (handle)
function (req)
try
response = handle (req)
# convert all responses to JSON
return HTTP . Response ( 200 , [], body = JSON3 . write (response))
catch error
@error " ERROR: " exception = (error, catch_backtrace ())
return HTTP . Response ( 500 , " The Server encountered a problem " )
end
end
end
# make sure 'myserializer' is the last middleware function in this list
serve (middleware = [myserializer], serialize = false )يتم إنشاء وثائق Swagger تلقائيًا لكل مسار تقوم بتسجيله في التطبيق الخاص بك. يتم إنشاء اسم المسار وأنواع المعلمات فقط و 200 و 500 استجابات تلقائيًا لك افتراضيًا.
يمكنك عرض الوثائق التي تم إنشاؤها على /docs ، ويمكن العثور على المخطط تحت /docs/schema . يمكن تغيير كلتا هذه القيم إلى ما تريد باستخدام وظيفة configdocs() . يمكنك أيضًا إلغاء الاشتراك في مستندات المولد تلقائيًا بالكامل عن طريق الاتصال بوظيفة disabledocs() قبل بدء تطبيقك.
لإضافة تفاصيل إضافية ، يمكنك إما استخدام وظائف mergeschema() أو setschema() المدمجة لتعديل المخطط بنفسك مباشرة أو دمج المخطط الذي تم إنشاؤه من حزمة SwaggerMarkdown.jl (أوصي بالاتحاد الأخير)
فيما يلي مثال على كيفية دمج المخطط الناتج عن حزمة SwaggerMarkdown.jl .
using Oxygen
using SwaggerMarkdown
# Here's an example of how you can merge autogenerated docs from SwaggerMarkdown.jl into your api
@swagger """
/divide/{a}/{b}:
get:
description: Return the result of a / b
parameters:
- name: a
in: path
required: true
description: this is the value of the numerator
schema:
type : number
responses:
'200':
description: Successfully returned an number.
"""
@get " /divide/{a}/{b} " function (req, a :: Float64 , b :: Float64 )
return a / b
end
# title and version are required
info = Dict ( " title " => " My Demo Api " , " version " => " 1.0.0 " )
openApi = OpenAPI ( " 3.0 " , info)
swagger_document = build (openApi)
# merge the SwaggerMarkdown schema with the internal schema
mergeschema (swagger_document)
# start the web server
serve ()فيما يلي مثال على كيفية تعديل المخطط يدويًا
using Oxygen
using SwaggerMarkdown
# Only the basic information is parsed from this route when generating docs
@get " /multiply/{a}/{b} " function (req, a :: Float64 , b :: Float64 )
return a * b
end
# Here's an example of how to update a part of the schema yourself
mergeschema ( " /multiply/{a}/{b} " ,
Dict (
" get " => Dict (
" description " => " return the result of a * b "
)
)
)
# Here's another example of how to update a part of the schema yourself, but this way allows you to modify other properties defined at the root of the schema (title, summary, etc.)
mergeschema (
Dict (
" paths " => Dict (
" /multiply/{a}/{b} " => Dict (
" get " => Dict (
" description " => " return the result of a * b "
)
)
)
)
) @get (path, func)| المعلمة | يكتب | وصف |
|---|---|---|
path | string أو router() | مطلوب . الطريق للتسجيل |
func | function | مطلوب . معالج الطلب لهذا الطريق |
تستخدم لتسجيل وظيفة في نقطة نهاية محددة للتعامل مع هذا النوع المقابل من الطلب
@route (methods, path, func)| المعلمة | يكتب | وصف |
|---|---|---|
methods | array | مطلوب . أنواع طلبات HTTP للتسجيل في هذا المسار |
path | string أو router() | مطلوب . الطريق للتسجيل |
func | function | مطلوب . معالج الطلب لهذا الطريق |
ماكرو منخفض المستوى يسمح للمسار بالتعامل مع أنواع الطلبات المتعددة
staticfiles (folder, mount)| المعلمة | يكتب | وصف |
|---|---|---|
folder | string | مطلوب . المجلد لتقديم الملفات من |
mountdir | string | نقطة نهاية الجذر لتركيب الملفات تحت (الافتراضي "ثابت") |
set_headers | function | تخصيص رؤوس استجابة HTTP عند إرجاع هذه الملفات |
loadfile | function | تخصيص السلوك عند تحميل الملفات |
خدمة جميع الملفات الثابتة داخل مجلد. تبحث هذه الوظيفة بشكل متكرر في دليل وتتصاعد جميع الملفات أسفل دليل MOUNT باستخدام مساراتها النسبية.
dynamicfiles (folder, mount)| المعلمة | يكتب | وصف |
|---|---|---|
folder | string | مطلوب . المجلد لتقديم الملفات من |
mountdir | string | نقطة نهاية الجذر لتركيب الملفات تحت (الافتراضي "ثابت") |
set_headers | function | تخصيص رؤوس استجابة HTTP عند إرجاع هذه الملفات |
loadfile | function | تخصيص السلوك عند تحميل الملفات |
خدمة جميع الملفات الثابتة داخل مجلد. تبحث هذه الوظيفة بشكل متكرر في دليل وتتصاعد جميع الملفات أسفل دليل MOUNT باستخدام مساراتها النسبية. يتم تحميل الملف على كل طلب ، وربما يلتقط أي تغييرات ملف.
html (content, status, headers)| المعلمة | يكتب | وصف |
|---|---|---|
content | string | مطلوب . السلسلة المراد إرجاعها كـ HTML |
status | integer | رمز استجابة HTTP (الافتراضي هو 200) |
headers | dict | رؤوس استجابة HTTP (الافتراضي لها رأس من نوع المحتوى تم تعيينه على "Text/HTML ؛ Charset = UTF-8") |
وظيفة المساعد لتعيين متى يجب إرجاع المحتوى كـ HTML
queryparams (request)| المعلمة | يكتب | وصف |
|---|---|---|
req | HTTP.Request | مطلوب . كائن طلب HTTP |
إرجاع معلمات الاستعلام من طلب كدليل ()
text (request)| المعلمة | يكتب | وصف |
|---|---|---|
req | HTTP.Request | مطلوب . كائن طلب HTTP |
إرجاع جسم الطلب كسلسلة
binary (request)| المعلمة | يكتب | وصف |
|---|---|---|
req | HTTP.Request | مطلوب . كائن طلب HTTP |
إرجاع جسم الطلب كملف ثنائي (إرجاع متجه UInt8 S)
json (request, classtype)| المعلمة | يكتب | وصف |
|---|---|---|
req | HTTP.Request | مطلوب . كائن طلب HTTP |
classtype | struct | هيكل لإلغاء تمييز كائن JSON في |
تخلص من جسم الطلب في بنية جوليا