
如何让 Python 写的 API 接口同时支持 Session 和 Token 认证?
本文将展示如何使用 Rust 构建一个完整的 gRPC API。通过本教程,你将学习如何:
在开始之前,请确保你已安装以下工具:
protoc
)cargo
包管理器选择一个工作目录,并使用 cargo
创建 Rust 项目:
cargo new demo
cd demo
此时项目目录结构已经就绪,可用于后续开发。
我们将创建一个 杂货店库存服务,支持:
proto/
目录:mkdir proto
proto/
下创建 store.proto
文件:syntax = "proto3";
package store;
service Inventory {
rpc Add(Item) returns (Response);
rpc Remove(ItemId) returns (Response);
rpc Get(ItemId) returns (Item);
rpc UpdateQuantity(UpdateRequest) returns (Response);
rpc UpdatePrice(UpdateRequest) returns (Response);
rpc Watch(WatchRequest) returns (stream Item);
}
message Item {
string id = 1;
string name = 2;
int32 quantity = 3;
float price = 4;
}
message ItemId {
string id = 1;
}
message UpdateRequest {
string id = 1;
int32 quantity = 2;
float price = 3;
}
message Response {
string message = 1;
}
message WatchRequest {
string filter = 1;
}
在 Cargo.toml
添加依赖:
[dependencies]
tonic = "0.7"
prost = "0.11"
[build-dependencies]
tonic-build = "0.7"
创建 build.rs
文件,用于在构建时自动生成 Rust 代码:
fn main() {
tonic_build::configure()
.build_server(true)
.build_client(true)
.out_dir("src/")
.compile(&["proto/store.proto"], &["proto"])
.unwrap();
}
生成代码:
cargo build
生成的 src/store.rs
包含客户端和服务器代码。
src/server.rs
:use tonic::{transport::Server, Request, Response, Status};
use std::sync::{Arc, Mutex};
use std::collections::HashMap;
mod store {
tonic::include_proto!("store");
}
use store::inventory_server::{Inventory, InventoryServer};
use store::{Item, ItemId, Response as StoreResponse, UpdateRequest};
#[derive(Default)]
pub struct StoreInventory {
inventory: Arc<Mutex<HashMap<String, Item>>>,
}
Inventory
服务方法:#[tonic::async_trait]
impl Inventory for StoreInventory {
async fn add(&self, request: Request<Item>) -> Result<Response<StoreResponse>, Status> {
let item = request.into_inner();
let mut inventory = self.inventory.lock().unwrap();
if inventory.contains_key(&item.id) {
return Err(Status::already_exists("Item already exists"));
}
inventory.insert(item.id.clone(), item);
Ok(Response::new(StoreResponse {
message: "Item added successfully".to_string(),
}))
}
}
async fn remove(&self, request: Request<ItemId>) -> Result<Response<StoreResponse>, Status> {
let id = request.into_inner().id;
let mut inventory = self.inventory.lock().unwrap();
if inventory.remove(&id).is_some() {
Ok(Response::new(StoreResponse {
message: "Item removed successfully".to_string(),
}))
} else {
Err(Status::not_found("Item not found"))
}
}
async fn update_quantity(&self, request: Request<UpdateRequest>) -> Result<Response<StoreResponse>, Status> {
let update = request.into_inner();
let mut inventory = self.inventory.lock().unwrap();
if let Some(item) = inventory.get_mut(&update.id) {
item.quantity += update.quantity;
Ok(Response::new(StoreResponse {
message: "Quantity updated successfully".to_string(),
}))
} else {
Err(Status::not_found("Item not found"))
}
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let addr = "[::1]:50051".parse()?;
let inventory = StoreInventory::default();
Server::builder()
.add_service(InventoryServer::new(inventory))
.serve(addr)
.await?;
Ok(())
}
async fn add_item(client: &mut store::inventory_client::InventoryClient<tonic::transport::Channel>,
id: &str, name: &str, quantity: i32, price: f32) {
let item = Item {
id: id.to_string(),
name: name.to_string(),
quantity,
price,
};
let response = client.add(Request::new(item)).await.unwrap();
println!("Response: {}", response.into_inner().message);
}
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut client = store::inventory_client::InventoryClient::connect("http://[::1]:50051").await?;
add_item(&mut client, "1", "Apple", 100, 1.5).await;
Ok(())
}
运行服务器和客户端:
cargo run --bin server
cargo run --bin client
测试添加、删除、更新商品,观察服务器响应。
通过本教程,你已经学会:
下一步,你可以尝试添加 TLS 加密、身份验证或流式订阅功能,提升服务安全性和功能丰富度。