引言:REST还是GraphQL——我们用Rust解决这个永恒的争论
如果您正在用Rust编写代码,并为您的API选择架构,那么您肯定问过自己:该选哪个——经典的REST还是时髦的GraphQL?两种方法都有一大批粉丝和一堆批评者。REST被指责数据冗余和客户端与服务器之间的“闲聊”。GraphQL则被指责缓存复杂和“地狱般”的数据库查询。
但让我们面对现实:Rust不是JavaScript,也不是Python。它的哲学是零成本抽象和极致性能。这是否意味着其中一种方法更适合Rust生态系统?让我们通过真实的代码示例来弄清楚。
在本文中,我们不仅仅列举优缺点。我们将用actix-web(REST)和async-graphql编写两个微服务,比较它们的性能、开发复杂度和维护便利性。开始吧!
快速入门:30秒了解REST和GraphQL
REST(表述性状态转移)是一种架构风格,其中每个资源(用户、文章、订单)都有自己的URL。您通过GET获取数据,通过POST创建数据,通过PUT/PATCH更新数据。一切简单且可预测。但通常您会得到比所需更多的数据,或者需要多次请求来收集相关实体。
GraphQL是一种查询语言和运行时环境。客户端自己决定需要哪些字段。一次请求——您就能精确得到所请求的内容。但这种灵活性需要付出服务器端实现复杂性和潜在性能问题的代价。
在Rust中,由于类型系统和异步特性,这两种方法都得到了最高水平的实现。但让我们深入探讨。
Rust中的REST:使用actix-web的经典方案
我们从熟悉的REST开始。以简单的博客为例:有用户、文章和评论。在REST中,我们需要创建几个端点。
use actix_web::{web, App, HttpServer, Responder, HttpResponse};use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize)]struct Article { id: i32, title: String, content: String, author_id: i32,}
async fn get_article(path: web::Path<i32>) -> impl Responder { let article_id = path.into_inner(); // 假设我们从数据库中获取文章 let article = Article { id: article_id, title: "GraphQL vs REST".to_string(), content: "详细对比...".to_string(), author_id: 1, }; HttpResponse::Ok().json(article)}
async fn get_articles_by_author(path: web::Path<i32>) -> impl Responder { let author_id = path.into_inner(); // 作者的文章列表 let articles = vec![ Article { id: 1, title: "文章1".to_string(), content: "正文...".to_string(), author_id }, Article { id: 2, title: "文章2".to_string(), content: "正文...".to_string(), author_id }, ]; HttpResponse::Ok().json(articles)}
#[actix_web::main]async fn main() -> std::io::Result<()> { HttpServer::new(|| { App::new() .route("/articles/{id}", web::get().to(get_article)) .route("/authors/{id}/articles", web::get().to(get_articles_by_author)) }) .bind("127.0.0.1:8080")? .run() .await}一切看起来干净明了。Rust中REST的优点:
- HTTP级别缓存简单(ETag、Cache-Control)。
- 结构透明——每个端点只做一件事。
- 易于与任何客户端集成(curl、Postman、浏览器)。
但也有缺点:
- N+1查询问题。要获取文章和作者,需要两次请求。或者创建一个带JOIN的单独端点。
- 过度获取。