OpenAPI é uma especificação de código aberto para descrever o design de interfaces de APIs RESTful. Ele define a estrutura de requisições e respostas da API, parâmetros, tipos de retorno, códigos de erro e outros detalhes em formatos JSON ou YAML, tornando a comunicação entre cliente e servidor mais clara e padronizada.
Originalmente, OpenAPI era a versão de código aberto da especificação Swagger, mas agora se tornou um projeto independente, com o apoio de muitas grandes empresas e desenvolvedores. Usar a especificação OpenAPI pode ajudar equipes de desenvolvimento a colaborar melhor, reduzir custos de comunicação e aumentar a eficiência. Além disso, OpenAPI oferece ferramentas para gerar automaticamente documentação de API, dados simulados (Mock) e casos de teste, facilitando o desenvolvimento e os testes.
O Salvo fornece integração com OpenAPI (modificado a partir do utoipa). O Salvo, aproveitando suas próprias características, extrai elegantemente informações de tipo OpenAPI automaticamente a partir de Handler
. O Salvo também integra interfaces OpenAPI populares como SwaggerUI, scalar, rapidoc e redoc.
Para lidar com nomes de tipos Rust longos, que podem não ser adequados para OpenAPI, o salvo-oapi
fornece o tipo Namer
, que permite definir regras personalizadas para alterar os nomes dos tipos no OpenAPI.
Código de Exemplo
Digite http://localhost:5800/swagger-ui
no navegador para ver a página do Swagger UI.
A integração do OpenAPI no Salvo é bastante elegante. Para o exemplo acima, em comparação com um projeto Salvo comum, apenas realizamos as seguintes etapas:
Ativar o recurso oapi
no Cargo.toml
: salvo = { workspace = true, features = ["oapi"] }
;
Substituir #[handler]
por #[endpoint]
;
Usar name: QueryParam<String, false>
para obter o valor da string de consulta. Ao acessar a URL http://localhost/hello?name=chris
, a string de consulta name
será analisada. O false
em QueryParam<String, false>
indica que o parâmetro é opcional. Se acessar http://localhost/hello
, não ocorrerá erro. Por outro lado, QueryParam<String, true>
indica que o parâmetro é obrigatório, caso contrário, retornará um erro.
Criar um OpenAPI
e o Router
correspondente. OpenApi::new("test api", "0.0.1").merge_router(&router)
significa que o OpenAPI
obtém as informações necessárias da documentação analisando um roteador e seus descendentes. Alguns roteadores podem não fornecer informações para gerar documentação e serão ignorados, como Handler
s definidos com #[handler]
em vez de #[endpoint]
. Ou seja, em projetos reais, por questões de progresso no desenvolvimento, você pode optar por não gerar documentação OpenAPI ou gerá-la parcialmente. Posteriormente, você pode aumentar gradualmente o número de interfaces OpenAPI geradas, e tudo o que precisa fazer é substituir #[handler]
por #[endpoint]
e ajustar a assinatura da função.
Através de use salvo::oapi::extract::*;
, você pode importar extratores de dados pré-definidos e comuns. Os extratores fornecem informações necessárias ao Salvo para gerar a documentação OpenAPI.
QueryParam<T, const REQUIRED: bool>
: Um extrator que obtém dados da string de consulta. QueryParam<T, false>
indica que o parâmetro é opcional. QueryParam<T, true>
indica que o parâmetro é obrigatório e, se não fornecido, retornará um erro.
HeaderParam<T, const REQUIRED: bool>
: Um extrator que obtém dados do cabeçalho da requisição. HeaderParam<T, false>
indica que o parâmetro é opcional. HeaderParam<T, true>
indica que o parâmetro é obrigatório e, se não fornecido, retornará um erro.
CookieParam<T, const REQUIRED: bool>
: Um extrator que obtém dados do cabeçalho da requisição. CookieParam<T, false>
indica que o parâmetro é opcional. CookieParam<T, true>
indica que o parâmetro é obrigatório e, se não fornecido, retornará um erro.
PathParam<T>
: Um extrator que obtém parâmetros de caminho da URL. Se este parâmetro não existir, a correspondência da rota falhará, portanto, não há cenário onde ele possa ser omitido.
FormBody<T>
: Obtém informações de um formulário enviado na requisição.
JsonBody<T>
: Obtém informações de uma carga útil JSON enviada na requisição.
#[endpoint]
Ao gerar documentação OpenAPI, use a macro #[endpoint]
no lugar da macro #[handler]
padrão. Ela é essencialmente uma versão aprimorada de #[handler]
.
Ela pode obter informações necessárias para gerar o OpenAPI a partir da assinatura da função.
Para informações que não podem ser fornecidas pela assinatura, você pode adicionar atributos diretamente na macro #[endpoint]
. As informações fornecidas dessa maneira serão mescladas com as obtidas da assinatura, e em caso de conflito, as informações dos atributos substituirão as da assinatura.
Você pode usar o atributo #[deprecated]
do Rust para marcar um Handler
como obsoleto. Embora o atributo #[deprecated]
suporte informações como motivo ou versão, o OpenAPI não as suporta, portanto, essas informações serão ignoradas na geração do OpenAPI.
Os comentários de documentação no código serão automaticamente extraídos para gerar o OpenAPI. A primeira linha é usada como summary
, e todo o comentário é usado como description
.
Você pode definir estruturas de dados usando #[derive(ToSchema)]
:
Você pode definir configurações opcionais com #[salvo(schema(...))]
:
example = ...
pode ser json!(...)
. json!(...)
será analisado por serde_json::json!
como serde_json::Value
.
xml(...)
pode ser usado para definir propriedades de objeto Xml:
Gera [parâmetros de caminho][path_parameters] a partir dos campos de uma estrutura.
Esta é uma implementação #[derive]
do trait [ToParameters
][to_parameters].
Normalmente, os parâmetros de caminho precisam ser definidos em [#[salvo_oapi::endpoint(...parameters(...))]
][path_parameters] do endpoint
. No entanto, ao usar uma [struct
][struct] para definir os parâmetros, essa etapa pode ser omitida. Ainda assim, se for necessário fornecer descrições ou alterar configurações padrão, parâmetros de caminho de [tipos primitivos
][primitive] e [String
][std_string] ou parâmetros de caminho no estilo [tupla] precisarão ser definidos em parameters(...)
.
Você pode usar o atributo #[deprecated]
do Rust para marcar um campo como obsoleto, o que será refletido na especificação OpenAPI gerada.
O atributo #[deprecated]
suporta informações adicionais, como motivo ou versão em que foi descontinuado, mas o OpenAPI não. O OpenAPI suporta apenas um valor booleano para indicar se está obsoleto. Embora seja possível declarar uma depreciação com um motivo, como #[deprecated = "Há uma maneira melhor de fazer isso"]
, o motivo não aparecerá na especificação OpenAPI.
Os comentários de documentação nos campos da estrutura serão usados como descrição dos parâmetros na especificação OpenAPI gerada.
#[salvo(parameters(...))]
Os seguintes atributos podem ser usados no atributo de contêiner #[salvo(parameters(…))]
de estruturas que derivam de ToParameters
:
names(...)
define uma lista separada por vírgulas de nomes para campos não nomeados da estrutura usados como parâmetros de caminho. Suportado apenas em estruturas não nomeadas.style = ...
define como todos os parâmetros são serializados, conforme especificado por [ParameterStyle
][style]. O padrão é baseado no atributo parameter_in
.default_parameter_in = ...
define a localização padrão dos parâmetros deste campo, com o valor vindo de [parameter::ParameterIn
][in_enum]. Se não fornecido, o padrão é query
.rename_all = ...
pode ser usado como alternativa ao rename_all
do serde
. Oferece a mesma funcionalidade.Use names
para definir nomes para um único parâmetro não nomeado.
Use names
para definir nomes para vários parâmetros não nomeados.
#[salvo(parameter(...))]
Os seguintes atributos podem ser usados em campos de estrutura com #[salvo(parameter(...))]
:
style = ...
define como o parâmetro é serializado por [ParameterStyle
][style]. O padrão é baseado no atributo parameter_in
.
parameter_in = ...
define onde o parâmetro deste campo está localizado, com o valor vindo de [parameter::ParameterIn
][in_enum]. Se não fornecido, o padrão é query
.
explode
define se novos pares parameter=value
são criados para cada parâmetro em um object
ou array
.
allow_reserved
define se caracteres reservados :/?#[]@!$&'()*+,;=
são permitidos no valor do parâmetro.
example = ...
pode ser uma referência a um método ou json!(...)
. O exemplo fornecido substitui qualquer exemplo do tipo subjacente.
value_type = ...
pode ser usado para substituir o tipo padrão usado para o campo na especificação OpenAPI. Útil quando o tipo padrão não corresponde ao tipo real, como ao usar tipos de terceiros não definidos em [ToSchema
][to_schema] ou [tipos primitivos][primitive]. O valor pode ser qualquer tipo Rust que normalmente seria serializado em JSON ou um tipo personalizado como Object
. Object
será renderizado como um objeto OpenAPI genérico.
inline
se habilitado, a definição do tipo deste campo deve vir de [ToSchema
][to_schema], e essa definição será incluída inline.
default = ...
pode ser uma referência a um método ou json!(...)
.
format = ...
pode ser uma variante do enum [KnownFormat
][known_format] ou um valor aberto como string. Por padrão, o formato é inferido a partir do tipo da propriedade de acordo com a especificação OpenAPI.
write_only
define que a propriedade é usada apenas para operações de escrita POST,PUT,PATCH e não para GET.
read_only
define que a propriedade é usada apenas para operações de leitura GET e não para POST,PUT,PATCH.
nullable
define se a propriedade pode ser null
(observação: isso é diferente de não ser obrigatória).
required = ...
usado para forçar que o parâmetro seja obrigatório. Veja as regras.
rename = ...
pode ser usado como alternativa ao rename
do serde
. Oferece a mesma funcionalidade.
multiple_of = ...
usado para definir um múltiplo do valor. O valor do parâmetro só é considerado válido se dividido por este valor resultar em um inteiro. O valor deve ser estritamente maior que 0
.
maximum = ...
usado para definir um limite superior inclusivo para o valor.
minimum = ...
usado para definir um limite inferior inclusivo para o valor.
exclusive_maximum = ...
usado para definir um limite superior exclusivo para o valor.
exclusive_minimum = ...
usado para definir um limite inferior exclusivo para o valor.
max_length = ...
usado para definir o comprimento máximo para valores do tipo string
.
min_length = ...
usado para definir o comprimento mínimo para valores do tipo string
.
pattern = ...
usado para definir uma expressão regular que o valor do campo deve corresponder. A expressão regular segue o padrão ECMA-262.
max_items = ...
pode ser usado para definir o número máximo de itens permitidos em um campo do tipo array
. O valor deve ser um inteiro não negativo.
min_items = ...
pode ser usado para definir o número mínimo de itens permitidos em um campo do tipo array
. O valor deve ser um inteiro não negativo.
with_schema = ...
usa um schema
criado por uma referência de função em vez do schema
padrão. A função deve satisfazer fn() -> Into<RefOr<Schema>>
. Não recebe parâmetros e deve retornar qualquer valor que possa ser convertido em RefOr<Schema>
.
additional_properties = ...
usado para definir tipos de forma livre para map
, como HashMap
e BTreeMap
. Tipos de forma livre permitem qualquer tipo nos valores do mapa. Os formatos suportados são additional_properties
e additional_properties = true
.
Algumas regras aplicadas aos atributos de campo ToParameters
para nulidade e obrigatoriedade também se aplicam aos atributos de campo ToSchema
. Veja as regras.
#[serde(...)]
A derivação ToParameters
atualmente suporta alguns [atributos serde][serde attributes]. Esses atributos suportados serão refletidos na documentação OpenAPI gerada. Atualmente, os seguintes atributos são suportados:
rename_all = "..."
suportado no nível do contêiner.rename = "..."
suportado apenas no nível do campo.default
suportado no nível do contêiner e do campo, de acordo com os [atributos serde][serde attributes].skip_serializing_if = "..."
suportado apenas no nível do campo.with = ...
suportado apenas no nível do campo.skip_serializing = "..."
suportado apenas no nível do campo ou variante.skip_deserializing = "..."
suportado apenas no nível do campo ou variante.skip = "..."
suportado apenas no nível do campo.Outros atributos serde
afetarão a serialização, mas não serão refletidos na documentação OpenAPI gerada.
_**Demonstra o uso do atributo de contêiner `#[salvo(parameters