O oxigênio é um micro-quadro trabalhado em cima da biblioteca http.jl. Respire calma, sabendo que você pode aumentar rapidamente um servidor da web com abstrações com as quais já está familiarizado.
Precisar de ajuda? Sinta -se à vontade para alcançar nossos canais de mídia social.
pkg > add OxygenCrie um servidor da Web com muito poucas linhas de código
using Oxygen
using HTTP
@get " /greet " function (req :: HTTP.Request )
return " hello world! "
end
# start the web server
serve ()Os manipuladores são usados para conectar seu código ao servidor de maneira limpa e direta. Eles atribuem um URL a uma função e invocam a função quando uma solicitação de entrada corresponde a esse URL.
do..endRequest por padrão quando nenhuma informação de tipo é fornecidaExistem 3 tipos de manipuladores suportados:
Request manipuladoresStreamWebsocket 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 São apenas funções, o que significa que existem muitas maneiras pelas quais eles podem ser expressos e definidos. Abaixo está um exemplo de várias maneiras diferentes de expressar e atribuir um manipulador 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 Os manipuladores de solicitação são usados para lidar com solicitações HTTP. Eles são definidos usando macros ou seus equivalentes de função e aceitam um objeto HTTP.Request como o primeiro argumento. Esses manipuladores suportam a sintaxe da função e do bloqueio.
@get , @post , @put , @patch , @delete , @routeget() , post() , put() , patch() , delete() , route() Os manipuladores de fluxo são usados para transmitir dados. Eles são definidos usando a Macro @stream ou a função stream() e aceitam um objeto HTTP.Stream como o primeiro argumento. Esses manipuladores suportam a sintaxe da função e do bloqueio.
@stream e stream() não exigem uma definição de tipo no primeiro argumento, eles assumem que é um fluxo.Stream podem ser atribuídos com macros e funções de roteamento padrão: @get , @post , etc.Stream Os manipuladores do WebSocket são usados para lidar com conexões do WebSocket. Eles são definidos usando a função @websocket Macro ou websocket() e aceitam um objeto HTTP.WebSocket como o primeiro argumento. Esses manipuladores suportam a sintaxe da função e do bloqueio.
@websocket e websocket() não exigem uma definição de tipo no primeiro argumento, eles assumem que é um webSocket.Websocket também podem ser atribuídos com a função @get macro ou get() , porque o protocolo WebSocket requer uma solicitação GET para iniciar o aperto de mão.Websocket Existem duas maneiras principais de registrar seus manipuladores de solicitação: as macros de roteamento padrão ou as funções de roteamento que utilizam a sintaxe do Block.
Para cada macro de roteamento, agora temos uma função de roteamento equivalente
@get -> get ()
@post -> post ()
@put -> put ()
@patch -> patch ()
@delete -> delete ()
@route -> route ()A única diferença prática entre os dois é que as macros de roteamento são chamadas durante o estágio de pré -compilação, enquanto as funções de roteamento são chamadas apenas quando invocadas. (As macros de roteamento chamam as funções de roteamento sob o capô)
# 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 O oxigênio, por padrão, identifica automaticamente o tipo de conteúdo do valor de retorno de um manipulador de solicitação ao criar uma resposta. Essa funcionalidade padrão é bastante útil, mas tem um impacto no desempenho. Em situações em que o tipo de retorno é conhecido, é recomendável usar uma das funções de renderização pré-existente para acelerar as coisas.
Aqui está uma lista das funções de renderização atualmente suportadas: html , text , json , file , xml , js , css , binary
Abaixo está um exemplo de como usar essas funções:
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 () Na maioria dos casos, essas funções aceitam seqüências simples como entradas. As únicas exceções são a função binary , que aceita um Vector{UInt8} e a função json que aceita qualquer tipo serializável.
Os parâmetros do caminho são declarados com aparelhos e são passados diretamente para o seu manipulador de solicitação.
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 ()Os parâmetros de consulta podem ser declarados diretamente dentro da assinatura dos manipuladores. Qualquer parâmetro que não seja mencionado dentro do caminho da rota é assumido como um parâmetro de consulta.
@get " /query " function (req :: HTTP.Request , a :: Int , message :: String = " hello world " )
return (a, message)
end Como alternativa, você pode usar a função queryparams() para extrair os valores brutos do URL como um dicionário.
@get " /query " function (req :: HTTP.Request )
return queryparams (req)
end Use a função formdata() para extrair e analisar os dados do formulário do corpo de uma solicitação. Esta função retorna um dicionário de pares de valor-chave do formulário
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 ()Todos os objetos são automaticamente desapegados no JSON usando a biblioteca JSON3
using Oxygen
using HTTP
@get " /data " function (req :: HTTP.Request )
return Dict ( " message " => " hello! " , " value " => 99.3 )
end
# start the web server
serve ()O oxigênio fornece alguma serialização e desserialização pronta para uso para a maioria dos objetos, mas requer o uso de estruttypes ao converter estruturas
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 ()O oxigênio vem com vários extratores embutidos projetados para reduzir a quantidade de placa de caldeira necessária para serializar entradas às funções do manipulador. Ao simplesmente definir uma estrutura e especificar a fonte de dados, esses extratores simplificam o processo de ingestão e validação de dados por meio de uma API uniforme.
payload@kwdefExtratores suportados:
Path - Extratos dos parâmetros do caminhoQuery - extratos de parâmetros de consulta,Header - Extratos dos cabeçalhos de solicitaçãoForm - Extratos formam dados do corpo de solicitaçãoBody - serializa o corpo de solicitação inteiro a um determinado tipo (string, float64, etc.)ProtoBuffer - extrai a mensagem ProtoBuf do corpo da solicitação (disponível através de uma extensão de pacote)Json - Extrair JSON do corpo de solicitaçãoJsonFragment - Extrai um "fragmento" do corpo JSON usando o nome do parâmetro para identificar e extrair a tecla de nível superior correspondente Neste exemplo, mostramos que o extrator Path pode ser usado juntamente com os parâmetros do caminho regular. Isso também funciona com parâmetros regulares de consulta e o extrator 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 Os valores padrão podem ser configurados com estruturas usando a 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 Além de serializar dados de entrada, você também pode definir suas próprias regras de validação usando a função validate . No exemplo abaixo, mostramos como usar os validadores global e local em seu código.
global antes de executar um validador 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 Você pode interpolar variáveis diretamente nos caminhos, o que faz com que o registro dinamicamente as rotas seja uma brisa
(Obrigado a @anandijain pela ideia)
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 () A função router() é um HOF (função de ordem superior) que permite reutilizar o mesmo prefixo e propriedades do caminho em vários pontos de extremidade. Isso é útil quando sua API começa a crescer e você deseja manter suas operações de caminho organizadas.
Abaixo estão os argumentos que a função do router() pode assumir:
router (prefix :: String ; tags :: Vector , middleware :: Vector , interval :: Real , cron :: String )tags - são usados para organizar pontos de extremidade nos documentos autogeneradosmiddleware - é usado para configurar o roteador e o middleware específico da rotainterval - é usado para apoiar ações repetidas ( chamando um manipulador de solicitação em um intervalo definido em segundos )cron - é usado para especificar uma expressão de Cron que determina quando chamar o manipulador de solicitação. 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 ()O oxigênio vem com um sistema de agendamento CRON embutido que permite chamar pontos de extremidade e funções automaticamente quando a expressão de Cron corresponde à hora atual.
Quando um trabalho é agendado, uma nova tarefa é criada e é executada em segundo plano. Cada tarefa usa sua expressão de cron e o tempo atual para determinar quanto tempo precisa dormir antes de ser executado.
O analisador Cron em oxigênio é baseado nas mesmas especificações que as usadas na primavera. Você pode encontrar mais informações sobre isso na página Spring Cron Expressions.
A seguir, é apresentada uma discriminação do que cada parâmetro em nossa expressão de Cron representa. Embora nossa especificação se pareça com a definida pela primavera, não é uma partida exata de 1 para 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)
│ │ │ │ │ │
* * * * * *
Expressões parciais também são suportadas, o que significa que as expressões subsequentes podem ser deixadas de fora (elas são inadimplentes para '*' ).
# 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 A função router() possui um argumento de palavra -chave chamado cron , que aceita uma expressão de Cron que determina quando um terminal é chamado. Assim como os outros argumentos de palavras -chave, ela pode ser reutilizada por pontos de extremidade que compartilham roteadores ou são substituídos por terminais herdados.
# 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 Além de agendar pontos de extremidade, você também pode usar a nova macro @cron para agendar funções. Isso é útil se você deseja executar o código em horários específicos sem torná -lo visível ou chamável na 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 Quando você é executado serve() ou serveparallel() , todos os trabalhos Cron registrados são iniciados automaticamente. Se o servidor for interrompido ou morto, todos os trabalhos de corrida também serão encerrados. Você pode interromper o servidor e todas as tarefas repetidas e trabalhos de cron chamando a função terminate() ou matando manualmente o servidor com ctrl+C .
Além disso, o oxigênio fornece funções de utilidade para iniciar e interromper manualmente os trabalhos de cron: startcronjobs() e stopcronjobs() . Essas funções também podem ser usadas fora de um servidor da web.
As tarefas repetidas fornecem uma API simples para executar uma função em um intervalo definido.
Existem duas maneiras de registrar tarefas repetidas:
interval em um router()@repeat É importante observar que os manipuladores de solicitação que usam essa propriedade não podem definir parâmetros de função adicionais fora do parâmetro HTTP.Request padrão.
No exemplo abaixo, o ponto de extremidade /repeat/hello é chamado a cada 0,5 segundos e "hello" é impresso no console a cada vez.
A função router() possui um parâmetro interval que é usado para chamar um manipulador de solicitação em um intervalo definido (em segundos).
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 ()Abaixo está um exemplo de como registrar uma tarefa repetida fora do roteador
@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 Quando o servidor é executado, todas as tarefas são iniciadas automaticamente. Mas o módulo também fornece utilitários para ter mais controle de granulação fina sobre as tarefas em execução usando as seguintes funções: starttasks() , stoptasks() e cleartasks()
O oxigênio pode se integrar à revisão para fornecer recarregamento a quente, acelerando o desenvolvimento. Como a Revise recomenda manter todo o código a ser revisado em um pacote, você precisa primeiro passar para esse tipo de layout.
Primeiro, verifique se o seu Project.toml possui os campos necessários, como name , para trabalhar em um pacote e não em um projeto.
Em seguida, escreva o código principal para suas rotas em um módulo src/MyModule.jl :
module MyModule
using Oxygen; @oxidise
@get "/greet" function(req::HTTP.Request)
return "hello world!"
end
end
Então você pode fazer um script debug.jl EntryPoint:
using Revise
using Oxygen
using MyModule
MyModule.serve(revise=:eager)
A opção revise também pode ser definida como :lazy ; nesse caso, as revisões sempre serão deixadas para apenas uma solicitação ser servida, em vez de ser tentada ansiosamente quando os arquivos de origem são alterados no disco.
Observe que você deve executar outro script de ponto de entrada sem revisar na produção.
Em alguns cenários avançados, pode ser necessário aumentar vários cortes na web dentro do mesmo módulo em portas diferentes. O oxigênio fornece uma maneira estática e dinâmica de criar várias instâncias de um servidor da Web.
Como regra geral, se você souber quantas instâncias precisa, com antecedência, é melhor ir com a abordagem estática.
@oxidiseO Oxygen fornece uma nova macro que possibilita a configuração e execução de várias instâncias. Ele gera métodos e os liga a um novo estado interno para o módulo atual.
No exemplo abaixo, dois servidores simples são definidos nos módulos A e B e são iniciados no módulo pai. Ambos os módulos contêm todas as funções exportadas do oxigênio que podem ser chamadas diretamente como mostrado abaixo.
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() A função instance ajuda a criar uma instância completamente independente de um servidor da Web de oxigênio em tempo de execução. Funciona criando dinamicamente um módulo Julia em tempo de execução e carregando o código de oxigênio dentro dele.
Todos os mesmos métodos de oxigênio estão disponíveis na instância nomeada. No exemplo abaixo, podemos usar o get e serve simplesmente usando a sintaxe do DOT na variável app1 para acessar os métodos subjacentes.
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 Para cenários em que você precisa lidar com quantidades mais altas de tráfego, você pode executar oxigênio em um modo multithread. Para utilizar esse modo, Julia deve ter mais de 1 thread para trabalhar. Você pode iniciar uma sessão de Julia com 4 tópicos usando o comando abaixo
julia --threads 4 serveparallel() inicia o servidor da web no modo de streaming e lida com solicitações em uma abordagem de multitarefa cooperativa. Esta função usa Threads.@spawn para agendar uma nova tarefa em qualquer thread disponível. Enquanto isso, o @async é usado dentro desta tarefa ao chamar cada manipulador de solicitação. Isso permite que a tarefa produz durante as operações de E/S.
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 () O oxigênio inclui uma extensão para o pacote protobuf.jl. Esta extensão fornece uma função protobuf() , simplificando o processo de trabalho com buffers de protocolo no contexto do servidor da web. Para uma melhor compreensão deste pacote, consulte sua documentação oficial.
Esta função tem sobrecargas para os seguintes cenários:
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)
endA seguir, é apresentado um exemplo de esquema que foi usado para criar as ligações de Julia necessárias. Essas ligações permitem a codificação e decodificação de mensagens no exemplo acima.
syntax = "proto3" ;
message Person {
string name = 1 ;
sint32 age = 2 ;
}
message People {
repeated Person people = 1 ;
} O oxigênio está equipado com várias extensões de embalagem que aprimoram seus recursos de plotagem. Essas extensões facilitam o retorno dos gráficos diretamente dos manipuladores de solicitação. Todas as operações são realizadas na memória usando um IoBuffer e retornam um HTTP.Response
Pacotes suportados e seus utilitários auxiliares:
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 () Em vez de construir um motor interno para modelar ou adicionar dependências adicionais, o oxigênio fornece duas extensões de embalagem para suportar os modelos de Mustache.jl e OteraEngine.jl .
O Oxygen fornece uma API de wrapper simples em torno de ambos os pacotes que facilitam a renderização de modelos de cordas, modelos e arquivos. Esta API Wrapper retorna uma função render que aceita um dicionário de entradas para preencher o modelo.
Em todos os cenários, o modelo renderizado é devolvido dentro de um objeto http.Response pronto para ser servido pela API. Por padrão, os tipos MIME são detectados automaticamente, observando o conteúdo do modelo ou o nome da extensão no arquivo. Se você conhece o tipo MIME, pode passá -lo diretamente através do argumento da palavra -chave mime_type para pular o processo de detecção.
Por favor, dê uma olhada na documentação do bigode.jl para aprender todos os recursos do pacote
Exemplo 1: Renderizando um modelo de bigode de um arquivo
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
endExemplo 2: Especificando o tipo MIME para um modelo de bigode simples
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
endPor favor, dê uma olhada na documentação oteraengine.jl para aprender os recursos completos do pacote
Exemplo 1: Renderizando um modelo Otera com lógica e loops
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
endNeste exemplo, um modelo Otera é definido com um loop for que itera uma lista de nomes, cumprimentando cada nome.
Exemplo 2: executando o código Julia no modelo Otera
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
Neste exemplo, um modelo Otera é definido com o código Julia incorporado que calcula o quadrado de um determinado número.
Você pode montar arquivos estáticos usando esta função útil que pesquise recursivamente uma pasta quanto a arquivos e monta tudo. Todos os arquivos são carregados na memória na inicialização.
using Oxygen
# mount all files inside the "content" folder under the "/static" path
staticfiles ( " content " , " static " )
# start the web server
serve ()Semelhante aos arquivos de static, essa função monta cada caminho e releia o arquivo para cada solicitação. Isso significa que quaisquer alterações nos arquivos após o início do servidor serão exibidas.
using Oxygen
# mount all files inside the "content" folder under the "/dynamic" path
dynamicfiles ( " content " , " dynamic " )
# start the web server
serve () Desativar o madeireiro interno pode fornecer alguns ganhos enormes de desempenho, o que pode ser útil em alguns cenários. Anedotalmente, eu vi uma aceleração 2-3x em serve() e uma aceleração de 4-5x no desempenho serveparallel() .
# This is how you disable internal logging in both modes
serve (access_log = nothing )
serveparallel (access_log = nothing ) O Oxygen fornece um formato de log padrão, mas permite personalizar o formato usando o parâmetro access_log . Essa funcionalidade está disponível nas funções serve() e serveparallel() .
Você pode ler mais sobre as opções de registro aqui
# 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 )As funções do middleware facilitam a criação de fluxos de trabalho personalizados para interceptar todas as solicitações de entrada e respostas de saída. Eles são executados na mesma ordem em que são passados (da esquerda para a direita).
Eles podem ser definidos no aplicativo, roteador e camada de rotear com o argumento da palavra -chave middleware . Todo o middleware é aditivo e qualquer middleware definido nessas camadas será combinado e executado.
O middleware sempre será executado na seguinte ordem:
application -> router -> route
Agora vamos ver algum middleware em ação:
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])Se você não quiser usar o serializador de resposta padrão do Oxygen, poderá desativá -lo e adicionar o seu! Basta criar sua própria função especial de middleware para serializar a resposta e adicioná -la no final de sua própria cadeia de middleware.
Ambos serve() e serveparallel() têm um argumento de palavra -chave serialize que pode eliminar o serializador padrão.
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 )A documentação do Swagger é gerada automaticamente para cada rota que você se registra em seu aplicativo. Somente o nome da rota, os tipos de parâmetros e as respostas 200 e 500 são criadas automaticamente para você por padrão.
Você pode visualizar sua documentação gerada em /docs , e o esquema pode ser encontrado em /docs/schema . Ambos os valores podem ser alterados para o que você deseja usando a função configdocs() . Você também pode optar por não participar inteiramente dos documentos autogenerados, chamando a função disabledocs() antes de iniciar seu aplicativo.
Para adicionar detalhes adicionais, você pode usar as funções de mergeschema() ou setschema() para modificar diretamente o esquema ou mesclar o esquema gerado do pacote SwaggerMarkdown.jl (eu recomendo o último)
Abaixo está um exemplo de como mesclar o esquema gerado no pacote 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 ()Abaixo está um exemplo de como modificar manualmente o esquema
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)| Parâmetro | Tipo | Descrição |
|---|---|---|
path | string ou router() | Obrigatório . A rota para se registrar |
func | function | Obrigatório . O manipulador de solicitação para esta rota |
Usado para registrar uma função em um terminal específico para lidar com esse tipo de solicitação correspondente
@route (methods, path, func)| Parâmetro | Tipo | Descrição |
|---|---|---|
methods | array | Obrigatório . Os tipos de solicitações HTTP para se registrar nesta rota |
path | string ou router() | Obrigatório . A rota para se registrar |
func | function | Obrigatório . O manipulador de solicitação para esta rota |
Macro de baixo nível que permite que uma rota seja lidar com vários tipos de solicitação
staticfiles (folder, mount)| Parâmetro | Tipo | Descrição |
|---|---|---|
folder | string | Obrigatório . A pasta para servir arquivos de |
mountdir | string | O terminal raiz para montar arquivos em (o padrão é "estático") |
set_headers | function | Personalize os cabeçalhos de resposta HTTP ao devolver esses arquivos |
loadfile | function | Personalize o comportamento ao carregar arquivos |
Sirva todos os arquivos estáticos em uma pasta. Essa função procura recursivamente um diretório e monta todos os arquivos no diretório de montagem usando seus caminhos relativos.
dynamicfiles (folder, mount)| Parâmetro | Tipo | Descrição |
|---|---|---|
folder | string | Obrigatório . A pasta para servir arquivos de |
mountdir | string | O terminal raiz para montar arquivos em (o padrão é "estático") |
set_headers | function | Personalize os cabeçalhos de resposta HTTP ao devolver esses arquivos |
loadfile | function | Personalize o comportamento ao carregar arquivos |
Sirva todos os arquivos estáticos em uma pasta. Essa função procura recursivamente um diretório e monta todos os arquivos no diretório de montagem usando seus caminhos relativos. O arquivo é carregado em cada solicitação, potencialmente pegando as alterações do arquivo.
html (content, status, headers)| Parâmetro | Tipo | Descrição |
|---|---|---|
content | string | Obrigatório . A string a ser devolvida como html |
status | integer | O código de resposta HTTP (o padrão é 200) |
headers | dict | Os cabeçalhos para a resposta HTTP (o padrão possui cabeçalho do tipo conteúdo definido como "text/html; charset = utf-8") |
Função ajudante para designar quando o conteúdo deve ser retornado como html
queryparams (request)| Parâmetro | Tipo | Descrição |
|---|---|---|
req | HTTP.Request | Obrigatório . O objeto de solicitação http |
Retorna os parâmetros de consulta de uma solicitação como um ditado ()
text (request)| Parâmetro | Tipo | Descrição |
|---|---|---|
req | HTTP.Request | Obrigatório . O objeto de solicitação http |
Retorna o corpo de um pedido como uma corda
binary (request)| Parâmetro | Tipo | Descrição |
|---|---|---|
req | HTTP.Request | Obrigatório . O objeto de solicitação http |
Retorna o corpo de uma solicitação como um arquivo binário (retorna um vetor de UInt8 s)
json (request, classtype)| Parâmetro | Tipo | Descrição |
|---|---|---|
req | HTTP.Request | Obrigatório . O objeto de solicitação http |
classtype | struct | Uma estrutura para desserializar um objeto JSON em |
Desserialize o corpo de uma solicitação em uma estrutura de Julia