Actix-web提供了一种用于类型安全的访问请求信息的工具,称为提取器(即impl FromRequest
)。
默认情况下,actix-web提供了几种提取器实现。
提取器可以作为handler函数的参数进行访问。 Actix-web每个handler函数最多支持10个提取器。 参数位置无关紧要。
async fn index(
path: web::Path<(String, String)>,
json: web::Json<MyInfo>,
) -> impl Responder {
format!("{} {} {} {}", path.0, path.1, json.id, json.username)
}
Path提供可以从请求路径中提取的信息。 您可以从path反序列化任何变量段。
例如,对于/users/{userid}/{friend}
路径注册的resource(资源),可以反序列化两个segments(段),即userid
和friend
。
这些段可以提取到一个元组中,即Path<(u32, String)>
或从serde crate实现Deserialize
特性的任何structure(结构体)。
use actix_web::{web, Result};
/// extract path info from "/users/{userid}/{friend}" url
/// {userid} - - deserializes to a u32
/// {friend} - deserializes to a String
async fn index(info: web::Path<(u32, String)>) -> Result<String> {
Ok(format!("Welcome {}, userid {}!", info.1, info.0))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
也有可能从serde提取path信息到实现Deserialize
(反序列化)特征的特定类型。
这是一个使用*serde*而不是*tuple*类型的等效示例。
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
userid: u32,
friend: String,
}
/// extract path info using serde
async fn index(info: web::Path<Info>) -> Result<String> {
Ok(format!("Welcome {}, userid {}!", info.friend, info.userid))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
也可以通过name来get
或 query
路径参数的请求:
async fn index(req: HttpRequest) -> Result<String> {
let name: String =
req.match_info().get("friend").unwrap().parse().unwrap();
let userid: i32 = req.match_info().query("userid").parse().unwrap();
Ok(format!("Welcome {}, userid {}!", name, userid))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().route(
"/users/{userid}/{friend}", // <- define path parameters
web::get().to(index),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
Query类型为请求的查询参数提供提取功能。 它的下面使用了serde_urlencoded crate。
use actix_web::web;
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
// this handler get called only if the request's query contains `username` field
async fn index(info: web::Query<Info>) -> String {
format!("Welcome {}!", info.username)
}
Json允许将请求body反序列化为struct(结构)。
要从请求的正文中(body)提取类型化的信息,类型T
必须从*serde*实现Deserialize
(反序列化) trait(特征)。
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body
async fn index(info: web::Json<Info>) -> Result<String> {
Ok(format!("Welcome {}!", info.username))
}
一些提取程序提供了一种配置提取过程的方法。
Json提取器的JsonConfig类型用于configuration(配置)。
要配置提取程序,请将其配置对象传递到resource的.data()
方法。
如果是Json提取器,它将返回*JsonConfig*。
您可以配置json payload的最大大小以及自定义错误处理函数。
以下示例将payload的大小限制为4kb,并使用自定义错误处理程序。
use actix_web::{error, web, FromRequest, HttpResponse, Responder};
use serde::Deserialize;
#[derive(Deserialize)]
struct Info {
username: String,
}
/// deserialize `Info` from request's body, max payload size is 4kb
async fn index(info: web::Json<Info>) -> impl Responder {
format!("Welcome {}!", info.username)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
HttpServer::new(|| {
App::new().service(
web::resource("/")
// change json extractor configuration
.app_data(web::Json::<Info>::configure(|cfg| {
cfg.limit(4096).error_handler(|err, _req| {
// create custom error response
error::InternalError::from_response(
err,
HttpResponse::Conflict().finish(),
)
.into()
})
}))
.route(web::post().to(index)),
)
})
.bind("127.0.0.1:8088")?
.run()
.await
}
目前仅支持url-encoded形式。
可以将url-encoded的正文提取为特定类型。
此类型必须从serde crate中执行Deserialize
trait。
FormConfig 允许配置提取过程。
use actix_web::{web, Result};
use serde::Deserialize;
#[derive(Deserialize)]
struct FormData {
username: String,
}
/// extract form data using serde
/// this handler gets called only if the content type is *x-www-form-urlencoded*
/// and the content of the request could be deserialized to a `FormData` struct
async fn index(form: web::Form<FormData>) -> Result<String> {
Ok(format!("Welcome {}!", form.username))
}
Actix-web还提供了其他几种提取器:
HttpRequest
- HttpRequest本身是一个提取程序,如果需要访问request,它会返回self。String
- 您可以将request(请求)的payload转换为字符串。文档strings中提供了示例。bytes::Bytes
- 您可以将request(请求)的payload转换为Bytes(字节)。文档strings中提供了示例。Payload
- 您可以访问request的payload。示例payloadexample可通过web::Data
extractor(提取器)从处理程序访问应用程序状态;
但是,状态可以作为只读引用进行访问。
如果您需要对状态的可变访问,则必须实现它。
当心,actix将创建应用程序状态和handlers(处理程序)的多个副本,这对于每个线程都是唯一的。 如果您在多个线程中运行应用程序,actix将创建与应用程序state对象和handler对象的线程数相同的数量。
这是存储处理的请求数的处理程序的示例:
use actix_web::{web, Responder};
use std::cell::Cell;
#[derive(Clone)]
struct AppState {
count: Cell<i32>,
}
async fn show_count(data: web::Data<AppState>) -> impl Responder {
format!("count: {}", data.count.get())
}
async fn add_one(data: web::Data<AppState>) -> impl Responder {
let count = data.count.get();
data.count.set(count + 1);
format!("count: {}", data.count.get())
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
let data = AppState {
count: Cell::new(0),
};
HttpServer::new(move || {
App::new()
.data(data.clone())
.route("/", web::to(show_count))
.route("/add", web::to(add_one))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
尽管此handler将起作用,但self.0
会有所不同,具体取决于线程数和每个线程处理的请求数。
适当的implementation(实现)将使用Arc
和AtomicUsize
。
use actix_web::{web, Responder};
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
#[derive(Clone)]
struct AppState {
count: Arc<AtomicUsize>,
}
async fn show_count(data: web::Data<AppState>) -> impl Responder {
format!("count: {}", data.count.load(Ordering::Relaxed))
}
async fn add_one(data: web::Data<AppState>) -> impl Responder {
data.count.fetch_add(1, Ordering::Relaxed);
format!("count: {}", data.count.load(Ordering::Relaxed))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
use actix_web::{App, HttpServer};
let data = AppState {
count: Arc::new(AtomicUsize::new(0)),
};
HttpServer::new(move || {
App::new()
.data(data.clone())
.route("/", web::to(show_count))
.route("/add", web::to(add_one))
})
.bind("127.0.0.1:8088")?
.run()
.await
}
注意诸如
Mutex
或RwLock
之类的同步原语。 actix-web框架异步处理请求。 通过阻止线程执行,所有并发请求处理过程都将被阻止。 如果需要从多个线程共享或更新某些状态,请考虑使用tokio synchronization primitives (同步原语)。