提取器

kayrx::web 提供了一种用于类型安全的请求信息访问的工具, 称为提取器. (即 impl FromRequest). 默认情况下,kayrx::web提供了几种提取器实现.

提取器可以作为处理函数的参数进行访问。kayrx::web 每个处理函数最多支持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}路径中注册的资源,可以反序列化两个段,useridfriend。这些片段可以提取到tuple,即 Path<(u32, String)>或者从Serde crate 中实现该Deserialize特征的任何结构

use kayrx::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)) } #[kayrx::main] async fn main() -> std::io::Result<()> { use kayrx::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 }

还可以提取路径信息(实现serdeDeserialize特征)到特定类型 。这是一个使用serde 而不是tuple类型的等效示例

use kayrx::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)) } #[kayrx::main] async fn main() -> std::io::Result<()> { use kayrx::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 }

它也可以通过路径名getquery请求的参数:

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)) } #[kayrx::main] async fn main() -> std::io::Result<()> { use kayrx::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

Query类型提供用于请求查询参数的提取功能。下面使用了serde_urlencoded crate.

use kayrx::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

Json 允许将请求主体反序列化为结构体。要从请求的正文中提取类型化的信息,类型T必须实现 serdeDeserialize 特征

use kayrx::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类型用于配置。要配置提取器,请将其配置对象传递给资源的.data()方法。如果是Json提取器,它将返回JsonConfig。您可以配置json有效负载的最大大小以及自定义错误处理函数.

use kayrx::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) } #[kayrx::main] async fn main() -> std::io::Result<()> { use kayrx::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 }

Form

目前仅支持url-encoded forms. 可以将url-encoded的正文提取为特定类型。此类型必须实现Serde Crate的Deserialize特征.

FormConfig允许配置提取过程

use kayrx::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)) }

其他

kayrx::web 还提供了其他几种提取器:

  • Data -如果您需要访问应用程序状态。
  • HttpRequest - HttpRequest本身是一个提取器,如果需要访问请求,它会返回self.
  • String -您可以将请求的有效负载转换为字符串. 示例
  • bytes :: Bytes-您可以将请求的有效负载转换为Byte. 示例
  • Payload -您可以访问请求的有效负载. 示例

应用状态

应用状态可通过web::Data提取器从处理程序访问;但是,状态作为只读引用进行访问。如果您需要对状态的可变访问,则必须实现它。

当心,kayrx::web将创建应用程序状态和处理程序的多个副本,这对于每个线程都是唯一的。如果您在多个线程中运行应用程序,kayrx::web将创建与线程数量相同的应用程序状态对象和处理程序对象。

这是存储处理请求数的处理程序示例:

use kayrx::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()) } #[kayrx::main] async fn main() -> std::io::Result<()> { use kayrx::web::{App, HttpServer}; let data = AppState { count: Cell::new(0), }; HttpServer::new(move || { App::new() .app_data(data.clone()) .route("/", web::to(show_count)) .route("/add", web::to(add_one)) }) .bind("127.0.0.1:8088")? .run() .await }

尽管此处理程序可以工作, 但self.0根据线程数和每个线程处理的请求数而有所不同。适当的实现将使用ArcAtomicUsize.

use kayrx::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)) } #[kayrx::main] async fn main() -> std::io::Result<()> { use kayrx::web::{App, HttpServer}; let data = AppState { count: Arc::new(AtomicUsize::new(0)), }; HttpServer::new(move || { App::new() .app_data(data.clone()) .route("/", web::to(show_count)) .route("/add", web::to(add_one)) }) .bind("127.0.0.1:8088")? .run() .await }

请注意同步原语,例如MutexRwLockkayrx::web框架异步处理请求。通过阻止线程执行,所有并发请求处理过程都将被阻止。如果需要从多个线程共享或更新某些状态,请考虑使用kayrx::krse::sync同步原语.