Bahasa Inggris 简体中文 繁體中文



Salvo adalah kerangka backend web karat yang sangat sederhana dan kuat. Hanya pengetahuan karat dasar yang diperlukan untuk mengembangkan layanan backend.
Anda dapat melihat sampel di sini, atau melihat situs web resmi.
Hanya perlu beberapa baris kode untuk mengimplementasikan server yang mendukung ACME untuk secara otomatis mendapatkan sertifikat, dan mendukung protokol HTTP1, HTTP2, dan HTTP3.
use salvo :: prelude :: * ;
# [ handler ]
async fn hello ( res : & mut Response ) {
res . render ( Text :: Plain ( "Hello World" ) ) ;
}
# [ tokio :: main ]
async fn main ( ) {
let mut router = Router :: new ( ) . get ( hello ) ;
let listener = TcpListener :: new ( "0.0.0.0:443" )
. acme ( )
. add_domain ( "test.salvo.rs" ) // Replace this domain name with your own.
. http01_challenge ( & mut router ) . quinn ( "0.0.0.0:443" ) ;
let acceptor = listener . join ( TcpListener :: new ( "0.0.0.0:80" ) ) . bind ( ) . await ;
Server :: new ( acceptor ) . serve ( router ) . await ;
}Tidak ada perbedaan antara pawang dan middleware, middleware hanyalah pawang. Anda dapat menulis middleware tanpa mengetahui konsep seperti tipe terkait dan tipe generik. Jika Anda dapat menulis fungsi, maka Anda dapat menulis middleware !!!
use salvo :: http :: header :: { self , HeaderValue } ;
use salvo :: prelude :: * ;
# [ handler ]
async fn add_header ( res : & mut Response ) {
res . headers_mut ( )
. insert ( header :: SERVER , HeaderValue :: from_static ( "Salvo" ) ) ;
}Kemudian tambahkan ke router:
Router :: new ( ) . hoop ( add_header ) . get ( hello ) Ini adalah middleware yang sangat sederhana, menambahkan Header ke Response , lihat kode sumber lengkap.
Biasanya kami menulis routing seperti ini:
Router :: with_path ( "articles" ) . get ( list_articles ) . post ( create_article ) ;
Router :: with_path ( "articles/<id>" )
. get ( show_article )
. patch ( edit_article )
. delete ( delete_article ) ;Seringkali, sesuatu seperti melihat artikel dan daftar artikel tidak memerlukan login pengguna, tetapi membuat, mengedit, menghapus artikel, dll. Membutuhkan izin otentikasi login pengguna. Sistem perutean seperti pohon di Salvo dapat memenuhi permintaan ini. Kami dapat menulis router tanpa login pengguna bersama:
Router :: with_path ( "articles" )
. get ( list_articles )
. push ( Router :: with_path ( "<id>" ) . get ( show_article ) ) ;Kemudian tulis router yang mengharuskan pengguna untuk masuk bersama, dan menggunakan middleware yang sesuai untuk memverifikasi apakah pengguna masuk:
Router :: with_path ( "articles" )
. hoop ( auth_check )
. push ( Router :: with_path ( "<id>" ) . patch ( edit_article ) . delete ( delete_article ) ) ; Meskipun kedua rute ini memiliki path("articles") , mereka masih dapat ditambahkan ke rute induk yang sama pada saat yang sama, sehingga rute terakhir terlihat seperti ini:
Router :: new ( )
. push (
Router :: with_path ( "articles" )
. get ( list_articles )
. push ( Router :: with_path ( "<id>" ) . get ( show_article ) ) ,
)
. push (
Router :: with_path ( "articles" )
. hoop ( auth_check )
. push ( Router :: with_path ( "<id>" ) . patch ( edit_article ) . delete ( delete_article ) ) ,
) ; <id> cocok dengan fragmen di jalur, dalam keadaan normal, id artikel hanyalah angka, yang dapat kita gunakan ekspresi reguler untuk membatasi aturan pencocokan id , r"<id:/d+/>" .
Anda juga dapat menggunakan <**> , <*+> atau <*?> Untuk mencocokkan semua fragmen jalur yang tersisa. Untuk membuat kode lebih mudah dibaca, Anda juga dapat menambahkan nama yang sesuai untuk membuat semantik path lebih jelas, misalnya: <**file_path> .
Beberapa ekspresi reguler untuk jalur pencocokan perlu sering digunakan, dan dapat didaftarkan sebelumnya, seperti GUID:
PathFilter :: register_wisp_regex (
"guid" ,
Regex :: new ( "[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}" ) . unwrap ( ) ,
) ;Ini membuatnya lebih ringkas saat pencocokan jalur diperlukan:
Router :: with_path ( "<id:guid>" ) . get ( index )Lihat Kode Sumber Lengkap
Kita bisa mendapatkan file async dengan file fungsi dalam Request :
# [ handler ]
async fn upload ( req : & mut Request , res : & mut Response ) {
let file = req . file ( "file" ) . await ;
if let Some ( file ) = file {
let dest = format ! ( "temp/{}" , file . name ( ) . unwrap_or_else ( || "file" . into ( ) ) ) ;
if let Err ( e ) = tokio :: fs :: copy ( & file . path , Path :: new ( & dest ) ) . await {
res . status_code ( StatusCode :: INTERNAL_SERVER_ERROR ) ;
} else {
res . render ( "Ok" ) ;
}
} else {
res . status_code ( StatusCode :: BAD_REQUEST ) ;
}
}Anda dapat dengan mudah mendapatkan data dari beberapa sumber data yang berbeda dan merakitnya menjadi jenis yang Anda inginkan. Anda dapat menentukan jenis kustom terlebih dahulu, misalnya:
# [ derive ( Serialize , Deserialize , Extractible , Debug ) ]
/// Get the data field value from the body by default.
# [ salvo ( extract ( default_source ( from = "body" ) ) ) ]
struct GoodMan < ' a > {
/// The id number is obtained from the request path parameter, and the data is automatically parsed as i64 type.
# [ salvo ( extract ( source ( from = "param" ) ) ) ]
id : i64 ,
/// Reference types can be used to avoid memory copying.
username : & ' a str ,
first_name : String ,
last_name : String ,
} Kemudian di Handler Anda bisa mendapatkan data seperti ini:
# [ handler ]
async fn edit ( req : & mut Request ) {
let good_man : GoodMan < ' _ > = req . extract ( ) . await . unwrap ( ) ;
}Anda bahkan dapat meneruskan jenis langsung ke fungsi sebagai parameter, seperti ini:
# [ handler ]
async fn edit < ' a > ( good_man : GoodMan < ' a > ) {
res . render ( Json ( good_man ) ) ;
}Lihat Kode Sumber Lengkap
Dukungan sempurna untuk OpenAPI dapat dicapai tanpa membuat perubahan signifikan pada proyek.
# [ derive ( Serialize , Deserialize , ToSchema , Debug ) ]
struct MyObject < T : ToSchema + std :: fmt :: Debug > {
value : T ,
}
# [ endpoint ]
async fn use_string ( body : JsonBody < MyObject < String > > ) -> String {
format ! ( "{:?}" , body )
}
# [ endpoint ]
async fn use_i32 ( body : JsonBody < MyObject < i32 > > ) -> String {
format ! ( "{:?}" , body )
}
# [ endpoint ]
async fn use_u64 ( body : JsonBody < MyObject < u64 > > ) -> String {
format ! ( "{:?}" , body )
}
# [ tokio :: main ]
async fn main ( ) {
tracing_subscriber :: fmt ( ) . init ( ) ;
let router = Router :: new ( )
. push ( Router :: with_path ( "i32" ) . post ( use_i32 ) )
. push ( Router :: with_path ( "u64" ) . post ( use_u64 ) )
. push ( Router :: with_path ( "string" ) . post ( use_string ) ) ;
let doc = OpenApi :: new ( "test api" , "0.0.1" ) . merge_router ( & router ) ;
let router = router
. push ( doc . into_router ( "/api-doc/openapi.json" ) )
. push ( SwaggerUi :: new ( "/api-doc/openapi.json" ) . into_router ( "swagger-ui" ) ) ;
let acceptor = TcpListener :: new ( "127.0.0.1:5800" ) . bind ( ) . await ;
Server :: new ( acceptor ) . serve ( router ) . await ;
}Salvo CLI adalah alat baris perintah yang menyederhanakan pembuatan proyek Salvo baru, templat pendukung untuk API Web, situs web, database (termasuk SQLite, PostgreSQL, dan MySQL melalui SQLX, SEAorm, Diesel, RBATIS), dan Middleware Dasar. Anda dapat menggunakan Salvo-Cli untuk membuat proyek Salvo baru:
cargo install salvo-clisalvo new project_nameAnda dapat menemukan lebih banyak contoh di folder contoh. Anda dapat menjalankan contoh -contoh ini dengan perintah berikut:
cd examples
cargo run --bin example-basic-auth Anda dapat menggunakan nama contoh apa pun yang ingin Anda jalankan alih-alih basic-auth di sini.
Hasil pengujian benchmark dapat ditemukan dari sini:
https://web-frameworks-benchmark.netlify.app/result?l=rust
https://www.techempower.com/benchmarks/#section=data-r22
Salvo adalah proyek open source. Jika Anda ingin mendukung Salvo, Anda dapat ☕ Membeli saya kopi di sini .
Salvo dilisensikan di bawah keduanya
Lisensi Apache, Versi 2.0, (lisensi-apache atau http://www.apache.org/licenses/license-2.0).
Lisensi MIT (lisensi-mit atau http://opensource.org/licenses/mit).