salvo rust解析请求数据的各种姿势

586 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第3天,点击查看活动详情

最近使用rust web框架salvo练习rust,为了巩固学习成果,总结一下如何使用salvo解析请求数据。项目如何搭建,可以查看salvo搭建rust web项目

也欢迎小伙伴来github上下载hello_salvo一起练习rust,觉得不错的话,给一个小星星哟!

GET请求

  • 从请求url中取参数

    // "/user/<id>?oid={order_id}"
    #[handler]
    async fn query_order_in_user(req: &mut Request, res: &mut Response) {
        // 从路径上取参数
        let user_id = req.param::<String>("id").unwrap();
        // 从?后面取参数
        let order_id = req.query::<String>("oid").unwrap();
        // 写出响应数据
        res.render(format!("user_id:{}\norder_id:{}", user_id, order_id));
    }
    
  • 实体类接收

    #[derive(Debug, Serialize, Deserialize, Extractible)]
    struct OrderQuery {
        // 从请求路径中解析,别名是“id”
        #[extract(alias = "id", source(from = "param"))]
        user_id: String,
        // 从?后面解析,别名是“oid”
        #[extract(alias = "oid", source(from = "query"))]
        order_id: String,
    }
    ​
    // /user/<id>?oid={order_id}
    #[handler]
    async fn query_order_in_user(query: OrderQuery, res: &mut Response) {
        // 从路径上取参数
        let user_id = query.user_id;
        // 从?后面取参数
        let order_id = query.order_id;
        // 写出响应数据
        res.render(format!("user_id:{}\norder_id:{}", user_id, order_id));
    }
    

    Router编写方式:

    Router::with_path("/user/<id>").get(query_order_in_user)
    

POST请求

先定义UserInfo结构体用来接收数据

#[derive(Debug, Serialize, Deserialize, Extractible)]
#[extract(
default_source(from = "body", format = "json")
)]
struct UserInfo {
    pub account: String,
    pub password: String,
}
  • 从请求体中解析数据

    #[handler]
    async fn create_account(req: &mut Request) -> Result<String, GlobalError> {
        // 从请求体中解析参数并创建UserInfo对象
        let user_info: UserInfo = req.extract_body().await.unwrap();
        // 取出数据
        let account = &user_info.account;
        let password = &user_info.password;
        // 调用业务逻辑
        match AccountService::add_account(account, password) {
            Ok(x) => {
                info!("受影响的行数:{}", x);
                Ok(String::from("成功!"))
            }
            Err(e) => Err(e)
        }
    }
    
  • 实体类接收数据

    // 自动从请求中取数据并实例化UserInfo对象
    #[handler]
    async fn create_account(user_info: UserInfo) -> Result<String, GlobalError> {
        let account = &user_info.account;
        let password = &user_info.password;
        match AccountService::add_account(account, password) {
            Ok(x) => {
                info!("受影响的行数:{}", x);
                Ok(String::from("成功!"))
            }
            Err(e) => Err(e)
        }
    }
    

    Router的编写方式:

    Router::with_path("/create_account").post(create_account)
    

    发生请求的方式:

    curl -X "POST" "http://127.0.0.1:7878/create_account" \
         -H 'Content-Type: application/json; charset=utf-8' \
         -d $'{
      "account": "helloworld",
      "password": "123456"
    }'
    
  • 同时从请求路径,url参数和请求体中取数据

    #[derive(Debug, Serialize, Deserialize, Extractible)]
    struct OrderSave {
        // 从请求路径中取参数"id"的值
        #[extract(alias = "id", source(from = "param"))]
        user_id: String,
        // 从?后面取参数"oid"的值
        #[extract(alias = "oid", source(from = "query"))]
        order_id: String,
        // 从请求体中取数据
        #[extract(source(from = "body", format = "json"))]
        title: String,
        #[extract(source(from = "body", format = "json"))]
        price: f64,
    }
    ​
    ​
    #[handler]
    async fn edit_order(order_save: OrderSave, res: &mut Response) {
        // 直接把解析好的数据输出到响应中
        res.render(Json(order_save));
    }
    

    Router编写方式:

    Router::with_path("/user/<id>").post(edit_order)
    

    请求方式:

    curl -X "POST" "http://127.0.0.1:7878/user/123456?oid=order_123456" \
         -H 'Content-Type: application/json; charset=utf-8' \
         -d $'{
      "title": "标题",
      "price": 18.88
    }'
    
  • 从请求头中取数据

    #[derive(Debug, Serialize, Deserialize, Extractible)]
    struct OrderSave {
        // 从请求路径中取参数"id"的值
        #[extract(alias = "id", source(from = "param"))]
        user_id: String,
        // 从?后面取参数"oid"的值
        #[extract(alias = "oid", source(from = "query"))]
        order_id: String,
        // 从请求体中取值
        #[extract(alias = "Cookie", source(from = "header"))]
        cookie:String,
        // 从请求体中取数据
        #[extract(source(from = "body", format = "json"))]
        title: String,
        #[extract(source(from = "body", format = "json"))]
        price: f64,
    }
    ​
    #[handler]
    async fn edit_order(order_save: OrderSave, req: &mut Request, res: &mut Response) {
        // 需要注意的是,参数中中划线的参数在实体类中暂时解析不出来,需要单独拎出来解析
        let content_type: String = req.header("Content-Type").unwrap();
        println!("{}", content_type);
        res.render(Json(order_save));
    }
    

    Router编写方式:

    Router::with_path("/user/<id>").post(edit_order)
    

    请求方式:

    curl -X "POST" "http://127.0.0.1:7878/user/123456?oid=order_123456" \
         -H 'Content-Type: application/json; charset=utf-8' \
         -H 'Cookie: JSESSIONID=FFF03011504BAD9F042BEBF4435E1CBD; SESSION=NjYxNzlhZjktZjViNi00NDE3LTgzMzAtNWQyZDQ0NGQwNzlm' \
         -d $'{
      "title": "标题",
      "price": 18.88
    }'
    
  • 文件上传(带参数)

    #[derive(Debug, Serialize, Deserialize, Extractible)]
    struct FileName {
        // 同时从请求体中解析出简单类型字段值
        #[extract(source(from = "body"))]
        name: String,
    }
    ​
    // "/upload/<id>"
    #[handler]
    async fn upload(file_name: FileName, req: &mut Request, res: &mut Response) {
        let id = req.param::<String>("id").unwrap();
        // 从请求体中取出文件
        let file = req.file("file").await;
        if let Some(file) = file {
            // 写入目标路径
            let dest_file = format!("/tmp/{}", &file.name().unwrap());
            if let Ok(x) = std::fs::copy(&file.path(), Path::new(&dest_file)) {
                // 返回响应
                res.render(format!("id:{} \nfile_name:{}  \n文件上传成功:{}", id, file_name.name, &dest_file));
            } else {
                // 写入文件失败
                GlobalError::new(500, "file store failure", "file store failure").write(res);
            }
        } else {
            // 没有取到文件,返回失败
            GlobalError::bad_request("file not found in request", "file not found in request").write(res);
        }
    }
    

    Router编写方式:

    Router::with_path("/upload/<id>").post(upload)
    

    请求方式:

    curl -X "POST" "http://127.0.0.1:7878/upload/123456" \
         -H 'Content-Type: multipart/form-data; charset=utf-8; boundary=__X_PAW_BOUNDARY__' \
         -H 'Cookie: JSESSIONID=FFF03011504BAD9F042BEBF4435E1CBD; SESSION=NjYxNzlhZjktZjViNi00NDE3LTgzMzAtNWQyZDQ0NGQwNzlm' \
         -F "file=" \
         -F "name=hello"
    

结束

到此,基本上大多数使用场景已经囊括进来了,小伙伴们可以根据以上这几个示例编写自己的业务代码。如果还有其他更复杂的应用场景,请参考salvo官方文档