OpenAPI è una specifica open source per descrivere il design delle interfacce delle API RESTful. Definisce in formato JSON o YAML la struttura di richieste e risposte, parametri, tipi di ritorno, codici di errore e altri dettagli, rendendo la comunicazione tra client e server più chiara e standardizzata.
Originariamente versione open source della specifica Swagger, OpenAPI è ora un progetto autonomo con il supporto di grandi aziende e sviluppatori. L'uso dello standard OpenAPI migliora la collaborazione nei team di sviluppo, riduce i costi di comunicazione e aumenta l'efficienza. Inoltre, fornisce strumenti per generare automaticamente documentazione API, dati mock e casi di test, facilitando sviluppo e testing.
Salvo offre integrazione con OpenAPI (modificata da utoipa). Sfruttando le sue caratteristiche, Salvo estrae elegantemente le informazioni sui tipi di dati OpenAPI direttamente dagli Handler
. Include anche interfacce OpenAPI popolari come SwaggerUI, scalar, rapidoc e redoc.
Per i nomi lunghi dei tipi Rust, non sempre adatti a OpenAPI, salvo-oapi
fornisce il tipo Namer
per personalizzare le regole e modificare i nomi dei tipi in OpenAPI.
Codice di esempio
Digitando http://localhost:5800/swagger-ui
nel browser si visualizza la pagina Swagger UI.
L'integrazione OpenAPI in Salvo è particolarmente elegante. Rispetto a un normale progetto Salvo, per l'esempio sopra bastano questi passaggi:
Abilitare la funzionalità oapi
in Cargo.toml
: salvo = { workspace = true, features = ["oapi"] }
;
Sostituire #[handler]
con #[endpoint]
;
Usare name: QueryParam<String, false>
per ottenere i valori dalla stringa di query. Visitando http://localhost/hello?name=chris
, il parametro name
viene analizzato. Il false
in QueryParam<String, false>
indica che il parametro è opzionale: http://localhost/hello
non darà errore. Con QueryParam<String, true>
, invece, il parametro è obbligatorio.
Creare OpenAPI
e il relativo Router
. OpenApi::new("test api", "0.0.1").merge_router(&router)
indica che OpenAPI
analizza un router e i suoi discendenti per ottenere le informazioni necessarie. Gli Handler
senza dati per la documentazione (come quelli definiti con #[handler]
invece di #[endpoint]
) vengono ignorati. In pratica, per motivi di sviluppo, puoi scegliere di non generare documentazione OpenAPI o farlo parzialmente, per poi espanderla gradualmente sostituendo #[handler]
con #[endpoint]
e modificando le firme delle funzioni.
Con use salvo::oapi::extract::*;
si importano estrattori di dati predefiniti. Forniscono a Salvo le informazioni necessarie per generare la documentazione OpenAPI.
QueryParam<T, const REQUIRED: bool>
: estrae dati dalla stringa di query. QueryParam<T, false>
indica parametro opzionale, QueryParam<T, true>
obbligatorio.
HeaderParam<T, const REQUIRED: bool>
: estrae dati dagli header. HeaderParam<T, false>
opzionale, HeaderParam<T, true>
obbligatorio.
CookieParam<T, const REQUIRED: bool>
: estrae dati dai cookie. CookieParam<T, false>
opzionale, CookieParam<T, true>
obbligatorio.
PathParam<T>
: estrae parametri dal percorso URL. Se assente, il routing fallisce.
FormBody<T>
: estrae dati da form inviati.
JsonBody<T>
: estrae dati da payload JSON.
#[endpoint]
Per generare documentazione OpenAPI, usa #[endpoint]
invece di #[handler]
. È una versione potenziata di #[handler]
.
Ricava dalle firme delle funzioni le informazioni necessarie per OpenAPI;
Per informazioni non disponibili nelle firme, si possono aggiungere attributi a #[endpoint]
. Questi si fondono con quelli delle firme, con priorità in caso di conflitto.
L'attributo #[deprecated]
di Rust segna un Handler come obsoleto. OpenAPI supporta solo la segnalazione di deprecazione, non i dettagli aggiuntivi.
I commenti di documentazione nel codice generano automaticamente OpenAPI: la prima riga diventa summary
, l'intero commento description
.
Usa #[derive(ToSchema)]
per definire strutture dati:
L'attributo #[salvo(schema(...))]
definisce opzioni:
example = ...
può essere json!(...)
. json!(...)
è analizzato da serde_json::json!
in serde_json::Value
.
xml(...)
definisce attributi per oggetti Xml:
Genera parametri di percorso dai campi di una struttura.
Implementazione #[derive]
del tratto ToParameters
.
Normalmente, i parametri di percorso vanno definiti in #[salvo_oapi::endpoint(...parameters(...))]
. Ma con le struct
, questo passo è opzionale. Tuttavia, per descrizioni o configurazioni, i parametri di tipo primitive
o String
richiedono ancora parameters(...)
.
L'attributo #[deprecated]
di Rust segna campi come obsoleti, riflessi in OpenAPI. OpenAPI supporta solo un valore booleano per l'obsoleto, ignorando dettagli aggiuntivi.
I commenti sui campi diventano descrizioni dei parametri in OpenAPI.
#[salvo(parameters(...))]
Gli attributi per strutture con #[derive(ToParameters)]
in #[salvo(parameters(…))]
:
names(...)
definisce nomi per campi senza nome, separati da virgole. Solo per strutture senza nome.style = ...
definisce la serializzazione con ParameterStyle
. Default basato su parameter_in
.default_parameter_in = ...
definisce la posizione predefinita con parameter::ParameterIn
. Default: query
.rename_all = ...
alternativa a rename_all
di serde
.Esempio con names
per un singolo parametro senza nome:
Per più parametri:
#[salvo(parameter(...))]
Attributi per campi con #[salvo(parameter(...))]
:
style = ...
: serializzazione con ParameterStyle
.parameter_in = ...
: posizione con parameter::ParameterIn
.explode
: crea coppie parameter=value
per oggetti/array.allow_reserved
: permette caratteri riservati :/?#[]@!$&'()*+,;=
.example = ...
: esempio che sovrascrive il tipo sottostante.value_type = ...
: sovrascrive il tipo predefinito in OpenAPI.inline
: il tipo del campo deve implementare ToSchema
e viene inlineato.default = ...
: valore predefinito.format = ...
: formato da KnownFormat
o stringa aperta.write_only
: solo per operazioni di scrittura.read_only
: solo per operazioni di lettura.nullable
: può essere null
.required = ...
: parametro obbligatorio.rename = ...
: rinomina campo, alternativo a serde
.multiple_of = ...
: valore deve essere multiplo di questo.maximum/minimum
: limiti inclusivi.exclusive_maximum/exclusive_minimum
: limiti esclusivi.max_length/min_length
: lunghezza stringa.pattern
: espressione regolare.max_items/min_items
: numero elementi array.with_schema = ...
: schema personalizzato da funzione.additional_properties = ...
: tipo libero per mappe.Le regole per campi nullable e required in ToParameters
seguono quelle di ToSchema
. Vedi regole.
#[serde(...)]
ToParameters
supporta alcuni attributi serde, riflessi in OpenAPI:
rename_all = "..."
a livello contenitore.rename = "..."
solo a livello campo.default
a livello contenitore o campo.skip_serializing_if = "..."
solo a livello campo.with = ...
solo a livello campo.skip_serializing/skip_deserializing
a livello campo o variante.skip = "..."
solo a livello campo.Altri attributi serde
influenzano la serializzazione ma non OpenAPI.
Mostra l'uso di #[salvo(parameters(...))]
con ToParameters
per parametri di percorso e inline di un campo query:
Sovrascrive tipo String
con i64
usando value_type
.
Sovrascrive String
con Object
, mostrato come type:object
in OpenAPI.
Sovrascrive con generici o altri tipi ToSchema
.
Validazione di valori con attributi
Schema manuale con schema_with
rename_all = ...
: regole per rinominare campi, simile a serde
. Se presenti entrambi, prevale #[serde(rename_all = "...")]
.
symbol = ...
: percorso del nome della struttura in OpenAPI, es. #[salvo(schema(symbol = "path.to.Pet"))]
.
default
: usa l'implementazione Default
della struttura per valori predefiniti.
Per applicazioni generiche, si definisce un tipo di errore globale (AppError) implementando Writer
o Scribe
per inviare messaggi di errore al client.
Per OpenAPI, si implementa EndpointOutRegister
per includere informazioni sugli errori:
Per filtrare specifici codici di stato in un Handler
: