Craft-Funktion
Craft ermöglicht Entwicklern die automatische Generierung von Handler-Funktionen und Endpunkten durch einfache Annotationen, während es nahtlos mit der OpenAPI-Dokumentationsgenerierung integriert wird.
Anwendungsfälle
Die Craft-Funktion ist besonders nützlich in folgenden Szenarien:
- Wenn Sie schnell Routen-Handler-Funktionen aus Struct-Methoden erstellen müssen
- Wenn Sie Boilerplate-Code für manuelle 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-Funktion zu verwenden, müssen Sie folgende Module importieren:
use salvo::oapi::extract::*;
use salvo::prelude::*;
Erstellen eines Service-Structs
Versehen Sie Ihren impl-Block mit der #[craft]-Makro-Annotation, um Struct-Methoden 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 von Handler-Funktionen
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
left- und right-Werte aus Query-Parametern extrahiert
- Auf
state vom Struct zugreift
- Das Berechnungsergebnis als String-Antwort zurückgibt
Erstellen von Endpunkten
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 zur Statusfreigabe nutzen, was besonders bei der Verarbeitung gleichzeitiger Anfragen nützlich ist.
Statische Endpunkte
Sie können auch statische Endpunkte erstellen, die nicht vom Instanzstatus 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 wird eine benutzerdefinierte Fehlerantwortbeschreibung hinzugefügt, die in der generierten OpenAPI-Dokumentation widergespiegelt wird.
Das oapi::extract-Modul von Salvo bietet verschiedene Parameter-Extraktoren, wobei die häufigsten sind:
QueryParam<T>: Extrahiert Parameter aus Query-Strings
PathParam<T>: Extrahiert Parameter aus URL-Pfaden
FormData<T>: Extrahiert Parameter aus Formulardaten
JsonBody<T>: Extrahiert Parameter aus JSON-Anfragekörpern
Diese Extraktoren behandeln automatisch Parameter-Parsing und Typkonvertierung, was das Schreiben von Handler-Funktionen erheblich vereinfacht.
Integration mit OpenAPI
Die Craft-Funktion kann automatisch API-Dokumentation generieren, die der OpenAPI-Spezifikation entspricht. 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("Beispiel-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 ist die API-Dokumentation unter dem Endpunkt /api-doc/openapi.json verfügbar und Swagger UI unter dem Pfad /swagger-ui erreichbar.
Vollständiges Beispiel
Nachfolgend ein vollständiges Beispiel, das zeigt, wie die Craft-Funktion verwendet wird, um drei verschiedene Typen von Endpunkten zu erstellen:
use salvo::oapi::extract::*;
use salvo::prelude::*;
use std::sync::Arc;
// Options struct holding a state value for calculations
#[derive(Clone)]
pub struct Opts {
state: i64,
}
// Implement methods for Opts using the craft macro for API generation
#[craft]
impl Opts {
// Constructor for Opts
fn new(state: i64) -> Self {
Self { state }
}
// Handler method that adds state value to two query parameters
#[craft(handler)]
fn add1(&self, left: QueryParam<i64>, right: QueryParam<i64>) -> String {
(self.state + *left + *right).to_string()
}
// Endpoint method using Arc for shared state
#[craft(endpoint)]
pub(crate) fn add2(
self: ::std::sync::Arc<Self>,
left: QueryParam<i64>,
right: QueryParam<i64>,
) -> String {
(self.state + *left + *right).to_string()
}
// Static endpoint method with custom error response
#[craft(endpoint(responses((status_code = 400, description = "Wrong request parameters."))))]
pub fn add3(left: QueryParam<i64>, right: QueryParam<i64>) -> String {
(*left + *right).to_string()
}
}
#[tokio::main]
async fn main() {
// Create shared state with initial value 1
let opts = Arc::new(Opts::new(1));
// Configure router with three endpoints:
// - /add1: Uses instance method with state
// - /add2: Uses Arc-wrapped instance method
// - /add3: Uses static method without state
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()));
// Generate OpenAPI documentation
let doc = OpenApi::new("Example API", "0.0.1").merge_router(&router);
// Add OpenAPI documentation and Swagger UI routes
let router = router
.push(doc.into_router("/api-doc/openapi.json"))
.push(SwaggerUi::new("/api-doc/openapi.json").into_router("swagger-ui"));
// Start server on localhost:8698
let acceptor = TcpListener::new("127.0.0.1:8698").bind().await;
Server::new(acceptor).serve(router).await;
}
[package]
name = "example-craft"
version.workspace = true
edition.workspace = true
publish.workspace = true
rust-version.workspace = true
[dependencies]
salvo = { workspace = true, features = ["craft", "oapi"] }
tokio = { workspace = true, features = ["macros"] }
tracing.workspace = true
tracing-subscriber.workspace = true