英語簡體中文簡體中文



Salvo是一個非常簡單和強大的Rust Web後端框架。開發後端服務只需要基本的生鏽知識。
您可以在此處查看樣本,或查看官方網站。
僅需幾行代碼即可實現支持ACME自動獲得證書的服務器,並且支持HTTP1,HTTP2和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 ;
}處理程序和中間件之間沒有區別,中間件只是一個處理程序。您可以在不知道相關類型和通用類型等概念的情況下編寫中間件。如果您可以編寫功能,那麼您可以編寫中間件! ! !
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" ) ) ;
}然後將其添加到路由器:
Router :: new ( ) . hoop ( add_header ) . get ( hello )這是一個非常簡單的中間件,它在Response中添加了Header ,查看完整的源代碼。
通常,我們以這樣的方式編寫路由:
Router :: with_path ( "articles" ) . get ( list_articles ) . post ( create_article ) ;
Router :: with_path ( "articles/<id>" )
. get ( show_article )
. patch ( edit_article )
. delete ( delete_article ) ;通常,查看文章和文章列表之類的內容不需要用戶登錄,而是創建,編輯,刪除文章等。需要用戶登錄身份驗證權限。 Salvo中的樹狀路由系統可以滿足這一需求。我們可以在沒有用戶登錄的情況下編寫路由器:
Router :: with_path ( "articles" )
. get ( list_articles )
. push ( Router :: with_path ( "<id>" ) . get ( show_article ) ) ;然後編寫需要用戶登錄的路由器,並使用相應的中間件來驗證用戶是否已登錄:
Router :: with_path ( "articles" )
. hoop ( auth_check )
. push ( Router :: with_path ( "<id>" ) . patch ( edit_article ) . delete ( delete_article ) ) ;儘管這兩條路線具有相同的path("articles") ,但它們仍然可以同時添加到同一父路線,因此最終路線看起來像這樣:
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>在正常情況下,匹配路徑中的片段,文章id只是一個數字,我們可以使用正則表達式來限制id匹配規則, r"<id:/d+/>" 。
您也可以使用<**> , <*+>或<*?>匹配所有剩餘路徑片段。為了使代碼更易讀,您還可以添加適當的名稱以使路徑語義更清晰,例如: <**file_path> 。
需要經常使用一些匹配路徑的正則表達式,並且可以提前註冊,例如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 ( ) ,
) ;當需要路徑匹配時,這使其更加簡潔:
Router :: with_path ( "<id:guid>" ) . get ( index )查看完整的源代碼
我們可以在Request中通過函數file獲取文件async:
# [ 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 ) ;
}
}您可以輕鬆地從多個不同的數據源獲取數據,並將其組裝成所需的類型。您可以首先定義自定義類型,例如:
# [ 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 ,
}然後,在Handler中,您可以獲取這樣的數據:
# [ handler ]
async fn edit ( req : & mut Request ) {
let good_man : GoodMan < ' _ > = req . extract ( ) . await . unwrap ( ) ;
}您甚至可以將類型直接傳遞給該函數作為參數,例如:
# [ handler ]
async fn edit < ' a > ( good_man : GoodMan < ' a > ) {
res . render ( Json ( good_man ) ) ;
}查看完整的源代碼
可以實現OpenAPI的完美支持,而無需對項目進行重大更改。
# [ 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是一種命令行工具,可簡化新的Salvo項目的創建,支持Web API,網站,數據庫的模板(包括SQLite,PostgreSQL和MySQL,通過SQLX,Seaorm,Seaorm,Diesel,diesel,rbatis)和基本中間件和基本中間件和基本中間件。您可以使用Salvo-CLI創建一個新的Salvo項目:
cargo install salvo-clisalvo new project_name您可以在示例文件夾中找到更多示例。您可以使用以下命令運行以下示例:
cd examples
cargo run --bin example-basic-auth您可以在此處使用任何要運行的示例名稱而不是basic-auth 。
從這裡可以找到基準測試結果:
https://web-frameworks-benchmark.netlify.app/result?l=rust
https://www.techempower.com/benchmarks/#section=data-r22
Salvo是一個開源項目。如果您想支持Salvo,可以在這裡給我買咖啡。
薩爾沃(Salvo
Apache許可證,版本2.0,(許可證或http://www.apache.org/licenses/license-2.0)。
麻省理工學院許可證(許可-MIT或http://opensource.org/licenses/mit)。