Кислород-это микрофрамочная работа, построенная на вершине библиотеки 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..endRequest по умолчанию, когда не предоставлена информация о типеЕсть 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 в качестве первого аргумента. Эти обработчики поддерживают как функцию, так и синтаксис Do Block.
@get , @post , @put , @patch , @delete , @routeget() , post() , put() , patch() , delete() , route() Обработчики потока используются для потоковой передачи данных. Они определяются с использованием функции @stream Macro или stream() и принимают объект HTTP.Stream в качестве первого аргумента. Эти обработчики поддерживают как функцию, так и синтаксис Do Block.
@stream и stream() не требуют определения типа в первом аргументе, они предполагают, что это поток.Stream могут быть назначены со стандартными макросами и функциями маршрутизации: @get , @post и т. Д.Stream Обработчики WebSocket используются для обработки соединений WebSocket. Они определяются с использованием макроса @websocket или функции websocket() и принимают объект HTTP.WebSocket в качестве первого аргумента. Эти обработчики поддерживают как функцию, так и синтаксис Do Block.
@websocket и websocket() не требует определения типа в первом аргументе, они предполагают, что это веб -сокет.Websocket также могут быть назначены с функцией @get Macro или 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 ()Кислород обеспечивает некоторую сериализацию и пустыню для большинства объектов.
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 ()Кислород поставляется с несколькими встроенными экстракторами, предназначенными для уменьшения количества шаблона, необходимой для сериализации входов на функции вашего обработчика. Просто определяя структуру и указав источник данных, эти экстракторы оптимизируют процесс приема данных и валидации с помощью равномерного API.
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 Значения по умолчанию можно настроить на структуру с использованием макроса @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 (функция более высокого порядка), которая позволяет повторно использовать один и тот же префикс и свойства пути в нескольких конечных точках. Это полезно, когда ваш API начинает расти, и вы хотите, чтобы ваш путь был организован.
Ниже приведены аргументы, которые может принять функция 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 ()Кислород поставляется со встроенной системой планирования 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 В дополнение к планированию конечных точек, вы также можете использовать новый макрос @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 .
Кроме того, кислород обеспечивает коммунальные функции для ручного запуска и остановки заданий Cron: startcronjobs() и stopcronjobs() . Эти функции также могут использоваться вне веб -сервера.
Повторные задачи обеспечивают простой API для запуска функции в установленном интервале.
Есть два способа зарегистрировать повторные задачи:
interval в router()@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()
Кислород может интегрироваться с пересмотром, чтобы обеспечить горячую перезагрузку, ускоряя разработку. Поскольку Repise рекомендует сохранить все код для пересмотра в пакете, вам сначала нужно перейти на этот тип макета.
Сначала убедитесь, что ваш Project.toml имеет необходимые поля, такие как name для работы над пакетом, а не проектом.
Далее напишите основной код для вас маршрутов в модуле src/MyModule.jl :
module MyModule
using Oxygen; @oxidise
@get "/greet" function(req::HTTP.Request)
return "hello world!"
end
end
Тогда вы можете сделать сценарий debug.jl ENTRYPOINT:
using Revise
using Oxygen
using MyModule
MyModule.serve(revise=:eager)
Опция revise также может быть установлен на :lazy , в этом случае пересмотра всегда останутся незадолго до того, как будет обслуживаться запрос, а не будет предпринимать с нетерпением, когда исходные файлы изменяются на диске.
Обратите внимание, что вам следует запустить еще один сценарий входа без пересмотра в производстве.
В некоторых расширенных сценариях вам может потребоваться развернуть несколько веб -северных конвертов в одном и том же модуле на разных портах. Кислород предоставляет как статический, так и динамичный способ создания нескольких экземпляров веб -сервера.
Как правило, если вы знаете, сколько случаев вам нужно заранее, лучше всего использовать статический подход.
@oxidiseКислород предоставляет новый макрос, который позволяет настройке и запускам нескольких экземпляров. Он генерирует методы и связывает их с новым внутренним состоянием для текущего модуля.
В приведенном ниже примере два простых сервера определяются в модулях 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 помогает вам создать совершенно независимый экземпляр кислородного веб -сервера во время выполнения. Он работает, динамически создавая модуль Юлии во время выполнения и загружая в него кислородный код.
Все те же методы от кислорода доступны под названным экземпляром. В приведенном ниже примере мы можем использовать get и serve , просто используя DOT Syntax на переменной 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 Для сценариев, в которых вам нужно обрабатывать большее количество трафика, вы можете запускать кислород в многопоточном режиме. Чтобы использовать этот режим, у Джулии должно быть более 1 потока для работы. Вы можете запустить сеанс Джулии с 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 () Кислород включает в себя расширение для пакета 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 ;
} Кислород оснащен несколькими расширениями упаковки, которые расширяют его возможности построения. Эти расширения позволяют легко возвращать участки непосредственно от обработчиков запросов. Все операции выполняются в памяти с использованием iObuffer и возвращают HTTP.Response
Поддерживаемые пакеты и их помощник UTILS:
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 () Вместо того, чтобы создавать внутренний двигатель для шаблона или добавления дополнительных зависимостей, кислород предоставляет два расширения упаковки для поддержки шаблонов Mustache.jl и OteraEngine.jl .
Кислород обеспечивает простой API обертки вокруг обоих пакетов, что позволяет легко рендеринговой шаблонах из строк, шаблонов и файлов. Этот API обертки возвращает функцию render , которая принимает словарь входов, чтобы заполнить шаблон.
Во всех сценариях шаблон визуализации возвращается внутри объекта http.response, готового к обслуживанию API. По умолчанию типы MIME автоматически определяются либо путем просмотра содержимого шаблона, либо имени расширения в файле. Если вы знаете тип MIME, вы можете передать его непосредственно через аргумент ключевого слова mime_type , чтобы пропустить процесс обнаружения.
Посмотрите на документацию Mustache.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Пожалуйста, посмотрите на документацию OtereraEngine.jl, чтобы узнать полные возможности пакета
Пример 1: рендеринг шаблона Oterera с логикой и петлями
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В этом примере шаблон Oterera определяется с помощью петли, которая итерации из списка имен, приветствуя каждое имя.
Пример 2: Запуск кода Юлии в шаблоне Oterera
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
В этом примере шаблон Oterera определяется с встроенным кодом Юлии, который вычисляет квадрат заданного числа.
Вы можете установить статические файлы, используя эту удобную функцию, которая рекурсивно ищет папку для файлов и все укорачивает. Все файлы загружаются в память при запуске.
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-3 раза в serve() и 4-5-кратное ускорение в 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])Если вы не хотите использовать Serializer ответа по умолчанию по умолчанию кислорода, вы можете выключить его и добавить свой собственный! Просто создайте свою специальную функцию промежуточного программного обеспечения для сериализации ответа и добавьте ее в конце собственной цепочки промежуточного программного обеспечения.
Оба 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, автоматически создаются для вас по умолчанию.
Вы можете просмотреть свою сгенерированную документацию AT /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 -запроса |
Возвращает параметры запроса из запроса в качестве DICT ()
text (request)| Параметр | Тип | Описание |
|---|---|---|
req | HTTP.Request | Необходимый . Объект HTTP -запроса |
Возвращает тело запроса в качестве строки
binary (request)| Параметр | Тип | Описание |
|---|---|---|
req | HTTP.Request | Необходимый . Объект HTTP -запроса |
Возвращает тело запроса в качестве двоичного файла (возвращает вектор UInt8 S)
json (request, classtype)| Параметр | Тип | Описание |
|---|---|---|
req | HTTP.Request | Необходимый . Объект HTTP -запроса |
classtype | struct | Структура, чтобы опустошать объект JSON в |
Десериализовать тело запроса в структуру Юлии