산소는 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 처리자Stream 핸들러Websocket 핸들러 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- 블록 구문을 모두 지원합니다.
@get , @post , @put , @patch , @delete , @routeget() , post() , put() , patch() , delete() , route() 스트림 핸들러는 데이터를 스트리밍하는 데 사용됩니다. @stream 매크로 또는 stream() 함수를 사용하여 정의되며 HTTP.Stream 객체를 첫 번째 인수로 받아들입니다. 이 핸들러는 기능과 DO- 블록 구문을 모두 지원합니다.
@stream and stream() 첫 번째 인수에 대한 유형 정의가 필요하지 않으며 스트림이라고 가정합니다.Stream 핸들러는 표준 라우팅 매크로 및 기능으로 할당 할 수 있습니다 : @get , @post 등Stream 핸들러로 식별 할 수 있도록 유형 정의를 명시 적으로 포함시켜야합니다. WebSocket 핸들러는 WebSocket 연결을 처리하는 데 사용됩니다. @websocket 매크로 또는 websocket() 함수를 사용하여 정의되며 첫 번째 인수로 HTTP.WebSocket 객체를 수락합니다. 이 핸들러는 기능과 DO- 블록 구문을 모두 지원합니다.
@websocket 및 websocket() 첫 번째 인수에 대한 유형 정의가 필요하지 않으며 WebSocket이라고 가정합니다.GET 요청이 필요하기 때문에 Websocket 핸들러는 @get 매크로 또는 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 () 대부분의 경우 이러한 기능은 일반 문자열을 입력으로 받아들입니다. 유일한 예외는 Vector{UInt8} 허용하는 binary 함수와 직렬화 가능한 유형을 허용하는 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 ()모든 객체는 JSON3 라이브러리를 사용하여 자동으로 JSON으로 사소화됩니다.
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 - 전체 요청 본문을 주어진 유형 (String, 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 매크로를 사용하여 structs에서 기본값을 설정할 수 있습니다.
@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 유효성 검사기를 모두 사용하는 방법을 보여줍니다.
local 유효성 검사기를 실행하기 전에 global 유효성 검사기를 호출합니다. 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 표현식과 현재 시간을 사용하여 실행하기 전에 잠을자는 데 걸리는 시간을 결정합니다.
산소의 크론 파서는 봄에 사용 된 것과 동일한 사양을 기반으로합니다. 이에 대한 자세한 내용은 Spring Cron Expressions 페이지에서 찾을 수 있습니다.
다음은 CRON 표현식의 각 매개 변수가 나타내는 것의 분석입니다. 우리의 사양은 Spring에 의해 정의 된 것과 매우 유사하지만 정확한 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 작업이 자동으로 시작됩니다. 서버가 중지되거나 죽으면 모든 실행중인 작업도 종료됩니다. terminate() 함수를 호출하거나 ctrl+C 로 서버를 수동으로 죽임으로써 서버와 모든 반복 작업 및 CRON 작업을 중지 할 수 있습니다.
또한 산소는 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()
산소는 개정과 통합되어 핫 재 장전을 제공하여 개발 속도를 높일 수 있습니다. 수정은 모든 코드를 패키지로 개정하도록 권장하므로 먼저이 유형의 레이아웃으로 이동해야합니다.
먼저 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 ,이 경우 소스 파일이 디스크에서 변경 될 때 간절히 시도하는 대신 요청이 제공되기 직전에 개정이 항상 남아 있습니다.
프로덕션에서 수정하지 않고 다른 EntryPoint 스크립트를 실행해야합니다.
일부 고급 시나리오에서는 다른 포트의 동일한 모듈 내에서 여러 웹 세버를 돌려야 할 수도 있습니다. 산소는 웹 서버의 여러 인스턴스를 생성하는 정적 및 동적 방법을 제공합니다.
일반적으로, 사전에 필요한 인스턴스 수를 알고 있다면 정적 접근 방식을 사용하는 것이 가장 좋습니다.
@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 기능을 사용하면 런타임에 산소 웹 서버의 완전히 독립적 인 인스턴스를 만드는 데 도움이됩니다. 런타임에 Julia 모듈을 동적으로 생성하고 그 안에 산소 코드를로드하여 작동합니다.
산소와 동일한 방법은 명명 된 인스턴스에서 사용할 수 있습니다. 아래의 예에서는 get 을 사용하고 app1 변수의 DOT 구문을 사용하여 기본 방법에 액세스하여 serve 수 있습니다.
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 더 많은 양의 트래픽을 처리 해야하는 시나리오의 경우 멀티 스레드 모드에서 산소를 실행할 수 있습니다. 이 모드를 활용하려면 Julia는 2 개 이상의 스레드가 있어야합니다. 아래 명령을 사용하여 4 개의 스레드로 Julia 세션을 시작할 수 있습니다.
julia --threads 4 serveparallel() 스트리밍 모드에서 웹 서버를 시작하고 협력적인 멀티 태스킹 접근법에서 요청을 처리합니다. 이 기능은 Threads.@spawn 사용하여 사용 가능한 모든 스레드에서 새 작업을 예약합니다. 한편 @ASYNC는 각 요청 핸들러를 호출 할 때이 작업 내에서 사용됩니다. 이를 통해 I/O 작업 중에 작업이 생성 될 수 있습니다.
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다음은 필요한 Julia 바인딩을 만드는 데 사용 된 스키마의 예입니다. 이러한 바인딩은 위의 예에서 메시지의 인코딩 및 디코딩을 허용합니다.
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 함수를 반환합니다.
모든 시나리오에서 렌더링 된 템플릿은 API에서 제공 할 준비가 된 HTTP.Response 객체 내부에서 반환됩니다. 기본적으로 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패키지의 전체 기능을 배우려면 oteraengine.jl 문서를 살펴보십시오.
예 1 : 논리와 루프로 오 테라 템플릿 렌더링
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이 예에서, 오 테라 템플릿은 이름 목록을 반복하여 각 이름을 인사하는 for 루프로 정의됩니다.
예제 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
이 예에서 오 테라 템플릿은 주어진 숫자의 제곱을 계산하는 임베디드 줄리아 코드로 정의됩니다.
이 편리한 기능을 사용하여 정적 파일을 장착하여 파일의 폴더를 재귀 적으로 검색하고 모든 것을 마운트합니다. 모든 파일은 시작시 메모리에로드됩니다.
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 () 내부 로거를 비활성화하면 일부 시나리오에서 도움이 될 수있는 대규모 성능 향상을 제공 할 수 있습니다. 일화 적으로, 나는 serve() 에서 2-3 배 속도를 보였고 serveparallel() 성능의 4-5 배 속도를 보았습니다.
# 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 | 파일을로드 할 때 동작을 사용자 정의하십시오 |
폴더 내의 모든 정적 파일을 제공합니다. 이 기능은 디렉토리를 재귀 적으로 검색하고 상대 경로를 사용하여 마운트 디렉토리의 모든 파일을 마운트합니다.
dynamicfiles (folder, mount)| 매개 변수 | 유형 | 설명 |
|---|---|---|
folder | string | 필수의 . 파일을 제공하는 폴더 |
mountdir | string | 아래에 마운트 파일에 대한 루트 엔드 포인트 (기본값은 "정적"입니다) |
set_headers | function | 이 파일을 반환 할 때 HTTP 응답 헤더를 사용자 정의하십시오 |
loadfile | function | 파일을로드 할 때 동작을 사용자 정의하십시오 |
폴더 내의 모든 정적 파일을 제공합니다. 이 기능은 디렉토리를 재귀 적으로 검색하고 상대 경로를 사용하여 마운트 디렉토리의 모든 파일을 마운트합니다. 파일은 각 요청에 따라로드되어 파일 변경이 잠재적으로 선택됩니다.
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 의 벡터를 반환).
json (request, classtype)| 매개 변수 | 유형 | 설명 |
|---|---|---|
req | HTTP.Request | 필수의 . HTTP 요청 객체 |
classtype | struct | JSON 객체를 제조하는 구조물 |
요청의 본문을 줄리아 구조물로 삼아