在2022年选择哪个Rust web框架?
poem
hyper
rocket
tide
rouille
iron
...
一个好的web框架既需要一个好的社区来帮助你,通过第三方包的使用方式来直接使用它们,以节省你的时间或寻找灵感。
1#[derive(Debug, Serialize, Deserialize)]
2struct Hello {
3 message: String,
4}
5
6async fn index(item: web::Json<Hello>) -> HttpResponse {
7 HttpResponse::Ok().json(item.message) // <- send response
8}
9
10#[actix_web::main]
11async fn main() -> std::io::Result<()> {
12 HttpServer::new(|| {
13 App::new()
14 .service(web::resource("/").route(web::post().to(index)))
15 })
16 .bind(("127.0.0.1", 8080))?
17 .run()
18 .await
19}
1#[derive(Debug, Serialize, Deserialize)]
2struct Hello {
3 message: String,
4}
5
6async fn index(item: Hello) -> Result<impl warp::Reply, Infallible> {
7 Ok(warp::reply::json(&hello.message))
8}
9
10#[tokio::main]
11async fn main() {
12 let promote = warp::post()
13 .and(warp::body::json())
14 .map(index);
15
16 warp::serve(promote).run(([127, 0, 0, 1], 8080)).await
17}
1#[derive(Debug, Serialize, Deserialize)]
2struct Hello {
3 message: String,
4}
5
6async fn index(item: Json<Hello>) ->impl IntoResponse { {
7 Json(item.message)
8}
9
10#[tokio::main]
11async fn main() {
12 let app = Router::new().route("/", post(index));
13
14 let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
15 axum::Server::bind(&addr)
16 .serve(app.into_make_service())
17 .await
18 .unwrap();
19}
1fn main() {
2 App::new()
3 .service(web::resource("/").route(web::get().to(api::list)))
4 .service(web::resource("/todo").route(web::post().to(api::create)))
5 .service(web::resource("/todo/{id}")
6 .route(web::post().to(api::update))
7 .route(web::delete().to(api::delete)),
8 );
9}
1pub fn todos() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
2 todos_list(db.clone())
3 .or(todos_create(db.clone()))
4 .or(todos_update(db.clone()))
5 .or(todos_delete(db))
6}
7
8pub fn todos_list() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
9 warp::path!("todos")
10 .and(warp::get())
11 .and(warp::query::<ListOptions>())
12 .and_then(handlers::list_todos)
13}
14
15pub fn todos_create() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
16 warp::path!("todos")
17 .and(warp::post())
18 .and(json_body())
19 .and_then(handlers::create_todo)
20}
21
22pub fn todos_update() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
23 warp::path!("todos" / u64)
24 .and(warp::put())
25 .and(json_body())
26 .and_then(handlers::update_todo)
27}
28
29pub fn todos_delete() -> impl Filter<Extract = impl warp::Reply, Error = warp::Rejection> + Clone {
30 warp::path!("todos" / u64)
31 .and(warp::delete())
32 .and_then(handlers::delete_todo)
33}
34
35fn main() {
36 let api = filters::todos(db);
37 warp::serve(api).run(([127, 0, 0, 1], 8080)).await
38}
1let app = Router::new()
2 .route("/todos", get(todos_list).post(todos_create))
3 .route("/todos/:id", patch(todos_update).delete(todos_delete));
1pub struct SayHi;
2
3impl<S, B> Transform<S, ServiceRequest> for SayHi
4where
5 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
6 S::Future: 'static,
7 B: 'static,
8{
9 type Response = ServiceResponse<B>;
10 type Error = Error;
11 type InitError = ();
12 type Transform = SayHiMiddleware<S>;
13 type Future = Ready<Result<Self::Transform, Self::InitError>>;
14
15 fn new_transform(&self, service: S) -> Self::Future {
16 ready(Ok(SayHiMiddleware { service }))
17 }
18}
19
20pub struct SayHiMiddleware<S> {
21 service: S,
22}
23
24impl<S, B> Service<ServiceRequest> for SayHiMiddleware<S>
25where
26 S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
27 S::Future: 'static,
28 B: 'static,
29{
30 type Response = ServiceResponse<B>;
31 type Error = Error;
32 type Future = LocalBoxFuture<'static, Result<Self::Response, Self::Error>>;
33
34 dev::forward_ready!(service);
35
36 fn call(&self, req: ServiceRequest) -> Self::Future {
37 println!("before");
38
39 let fut = self.service.call(req);
40
41 Box::pin(async move {
42 let res = fut.await?;
43 println!("after");
44 Ok(res)
45 })
46 }
47}
48
49#[actix_web::main]
50async fn main() -> std::io::Result<()> {
51 App::new()
52 .wrap(simple::SayHi)
53 .service(
54 web::resource("/").to(|| async {
55 "Hello, middleware! Check the console where the server is run."
56 }),
57 )
58}
1pub fn json_body<T: DeserializeOwned + Send>() -> impl Filter<Extract = (T,), Error = warp::Rejection> + Clone {
2 warp::body::content_length_limit(1024 * 16).and(warp::body::json())
3}
4
5
6fn main() {
7 let api = api.and(warp::path("jobs"))
8 .and(warp::path::end())
9 .and(warp::post())
10 .and(json_body())
11 .and_then(create_job);
12}
1#[derive(Clone)]
2struct MyMiddleware<S> {
3 inner: S,
4}
5
6impl<S> Service<Request<Body>> for MyMiddleware<S>
7where
8 S: Service<Request<Body>, Response = Response> + Clone + Send + 'static,
9 S::Future: Send + 'static,
10{
11 type Response = S::Response;
12 type Error = S::Error;
13 type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
14
15 fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
16 self.inner.poll_ready(cx)
17 }
18
19 fn call(&mut self, mut req: Request<Body>) -> Self::Future {
20 println!("before");
21 // best practice is to clone the inner service like this
22 // see https://github.com/tower-rs/tower/issues/547 for details
23 let clone = self.inner.clone();
24 let mut inner = std::mem::replace(&mut self.inner, clone);
25
26 Box::pin(async move {
27 let res: Response = inner.call(req).await?;
28
29 println!("after");
30
31 Ok(res)
32 })
33 }
34}
35
36fn main() {
37 let app = Router::new()
38 .route("/", get(|| async { /* ... */ }))
39 .layer(layer_fn(|inner| MyMiddleware { inner }));
40}
在构建web服务时,需要为外部服务共享一些变量,例如数据库连接池或一些服务的客户端。
1struct State {}
2
3async fn index(
4 state: Data<Arc<State>>,
5 req: HttpRequest,
6) -> HttpResponse {
7 // ...
8}
9
10#[actix_web::main]
11async fn main() -> io::Result<()> {
12 let state = Arc::new(State {});
13
14 HttpServer::new(move || {
15 App::new()
16 .app_data(state.clone())
17 .service(web::resource("/").to(index))
18 })
19 .bind(("127.0.0.1", 8080))?
20 .run()
21 .await
22}
1struct State {}
2
3pub fn with_state(
4 state: Arc<State>,
5) -> impl Filter<Extract = (Arc<State>,), Error = std::convert::Infallible> + Clone {
6 warp::any().map(move || state.clone())
7}
8
9pub async fn create_job(
10 state: Arc<AppState>,
11) -> Result<impl warp::Reply, warp::Rejection> {
12 // ...
13}
14
15fn main() {
16 let state = Arc::new(State{});
17 let api = api.and(warp::path("jobs"))
18 .and(warp::path::end())
19 .and(warp::post())
20 .and(with_state(state))
21 .and_then(create_job);
22}
1struct State {}
2
3async fn handler(
4 Extension(state): Extension<Arc<State>>,
5) {
6 // ...
7}
8
9fn main() {
10 let shared_state = Arc::new(State {});
11
12 let app = Router::new()
13 .route("/", get(handler))
14 .layer(AddExtensionLayer::new(shared_state));
15}
我最倾向于axum,我发现它有最干净的API,它构建在hyper之上,hyper是Rust中测试最多、最可靠的HTTP栈,而且因为它是由tokio的团队开发的。
对于大型项目,我认为actix-web是无可争议的赢家。