Craft-Funktionen
Craft ermöglicht Entwicklern die automatische Generierung von Handler-Funktionen und Endpunkten durch einfache Annotationen, während eine nahtlose Integration mit der OpenAPI-Dokumentationsgenerierung gewährleistet wird.
Anwendungsfälle
Die Craft-Funktionen sind besonders nützlich in folgenden Szenarien:
- Wenn Sie schnell Routen-Handler aus Strukturmethoden erstellen müssen
- Wenn Sie den manuellen Aufwand für Parameter-Extraktion und Fehlerbehandlung reduzieren möchten
- Wenn Sie automatisch OpenAPI-Dokumentation für Ihre API generieren müssen
- Wenn Sie Geschäftslogik vom Web-Framework entkoppeln möchten
Grundlegende Verwendung
Um die Craft-Funktionen zu nutzen, müssen folgende Module importiert werden:
use salvo::oapi::extract::*;
use salvo::prelude::*;
Erstellen einer Service-Struktur
Mit dem #[craft]
-Makro können Sie Ihren impl-Block annotieren, um Strukturmethoden in Handler-Funktionen oder Endpunkte umzuwandeln.
#[derive(Clone)]
pub struct Opts {
state: i64,
}
#[craft]
impl Opts {
// Konstruktor
fn new(state: i64) -> Self {
Self { state }
}
// Weitere Methoden...
}
Erstellen einer Handler-Funktion
Verwenden Sie #[craft(handler)]
, um eine Methode in eine Handler-Funktion umzuwandeln:
#[craft(handler)]
fn add1(&self, left: QueryParam<i64>, right: QueryParam<i64>) -> String {
(self.state + *left + *right).to_string()
}
Diese Methode wird zu einer Handler-Funktion, die:
- Automatisch die Werte
left
und right
aus den Query-Parametern extrahiert
- Auf den
state
-Zustand in der Struktur zugreift
- Das Berechnungsergebnis als String-Antwort zurückgibt
Erstellen eines Endpunkts
Verwenden Sie #[craft(endpoint)]
, um eine Methode in einen Endpunkt umzuwandeln:
#[craft(endpoint)]
pub(crate) fn add2(
self: ::std::sync::Arc<Self>,
left: QueryParam<i64>,
right: QueryParam<i64>,
) -> String {
(self.state + *left + *right).to_string()
}
Endpunkte können Arc
nutzen, um Zustände zu teilen, was besonders bei der Verarbeitung gleichzeitiger Anfragen hilfreich ist.
Statische Endpunkte
Sie können auch statische Endpunkte erstellen, die nicht vom Instanzzustand abhängen:
#[craft(endpoint(responses((status_code = 400, description = "Falsche Anfrageparameter."))))]
pub fn add3(left: QueryParam<i64>, right: QueryParam<i64>) -> String {
(*left + *right).to_string()
}
In diesem Beispiel wurde zusätzlich eine benutzerdefinierte Fehlerantwort hinzugefügt, die in der generierten OpenAPI-Dokumentation erscheint.
Das oapi::extract
-Modul von Salvo bietet verschiedene Parameter-Extraktoren, die gängigsten sind:
QueryParam<T>
: Extrahiert Parameter aus der Query-Zeichenkette
PathParam<T>
: Extrahiert Parameter aus dem URL-Pfad
FormData<T>
: Extrahiert Parameter aus Formulardaten
JsonBody<T>
: Extrahiert Parameter aus dem JSON-Request-Body
Diese Extraktoren übernehmen automatisch die Parameteranalyse und Typumwandlung, was das Schreiben von Handler-Funktionen erheblich vereinfacht.
Integration mit OpenAPI
Die Craft-Funktionen können automatisch OpenAPI-konforme API-Dokumentation generieren. Im Beispiel:
let router = Router::new()
.push(Router::with_path("add1").get(opts.add1()))
.push(Router::with_path("add2").get(opts.add2()))
.push(Router::with_path("add3").get(Opts::add3()));
// OpenAPI-Dokumentation generieren
let doc = OpenApi::new("Example API", "0.0.1").merge_router(&router);
// OpenAPI-Dokumentation und Swagger-UI-Routen hinzufügen
let router = router
.push(doc.into_router("/api-doc/openapi.json"))
.push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui"));
Mit dieser Konfiguration wird die API-Dokumentation unter dem Endpunkt /api-doc/openapi.json
bereitgestellt, und die Swagger-UI ist unter /swagger-ui
verfügbar.
Vollständiges Beispiel
Hier ist ein vollständiges Beispiel, das zeigt, wie Craft-Funktionen verwendet werden, um drei verschiedene Arten von Endpunkten zu erstellen:
use salvo::oapi::extract::*;
use salvo::prelude::*;
use std::sync::Arc;
#[derive(Clone)]
pub struct Opts {
state: i64,
}
#[craft]
impl Opts {
fn new(state: i64) -> Self {
Self { state }
}
#[craft(handler)]
fn add1(&self, left: QueryParam<i64>, right: QueryParam<i64>) -> String {
(self.state + *left + *right).to_string()
}
#[craft(endpoint)]
pub(crate) fn add2(
self: ::std::sync::Arc<Self>,
left: QueryParam<i64>,
right: QueryParam<i64>,
) -> String {
(self.state + *left + *right).to_string()
}
#[craft(endpoint(responses((status_code = 400, description = "Falsche Anfrageparameter."))))]
pub fn add3(left: QueryParam<i64>, right: QueryParam<i64>) -> String {
(*left + *right).to_string()
}
}
#[tokio::main]
async fn main() {
// Gemeinsamen Zustand erstellen, Startwert ist 1
let opts = Arc::new(Opts::new(1));
// Routen für drei Endpunkte konfigurieren
let router = Router::new()
.push(Router::with_path("add1").get(opts.add1()))
.push(Router::with_path("add2").get(opts.add2()))
.push(Router::with_path("add3").get(Opts::add3()));
// OpenAPI-Dokumentation generieren
let doc = OpenApi::new("Example API", "0.0.1").merge_router(&router);
// OpenAPI-Dokumentation und Swagger-UI-Routen hinzufügen
let router = router
.push(doc.into_router("/api-doc/openapi.json"))
.push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui"));
// Server auf localhost:5800 starten
let acceptor = TcpListener::new("127.0.0.1:5800").bind().await;
Server::new(acceptor).serve(router).await;
}