Actix-Web框架是目前性能上数一数二的rust的web框架,它的性能远超spring,nestJS, gin等目前流行的框架。

仅仅是返回hello,wold,我发现actix-web的性能就已经是gin的3倍。但是使用actix-web的缺点也非常明显,就是代码通过编译非常困难(也许是我太菜了)。

我实践了actix-web的以下功能:

  • 静态文件
  • 错误处理
  • post一个json参数

本服务器用于提交易班账号信息

首先在文件结构上

page页面用于存放静态页面,src内容是代码文件,configure_parse用于解析配置文件,配置文件格式是toml,db_handler用于处理数据库,response用于统一处理错误。APITEST用于测试接口。main.rs是主函数。开发过程中使用命令cargo watch -x run,可以热编译。需要安装:cargo install cargo-watch

大概需要下面这些依赖

[dependencies]
actix-web = "4"
actix-files = "0.6.0"
derive_more = "0.99.17"
sqlx = { version = "0.5.11", features = ["runtime-actix-native-tls", "mysql"] }
exitcode = "1.1.2"
serde = { version = "1.0.136" ,features=["derive"]}
serde_json = "1.0.79"
futures-util = { version = "0.3.7", default-features = false, features = ["std"] }
validator = { version = "0.12", features = ["derive"] }
toml = "0.5.8"

HTTP服务器

官方的API文档给出了一个简单的例子

use actix_web::{get, web, App, HttpServer, Responder};#[get("/hello/{name}")]
async fn greet(name: web::Path<String>) -> impl Responder {format!("Hello {name}!")
}#[actix_web::main] // or #[tokio::main]
async fn main() -> std::io::Result<()> {HttpServer::new(|| {App::new().route("/hello", web::get().to(|| async { "Hello World!" })).service(greet)}).bind(("127.0.0.1", 8080))?.run().await
}

在宏的加持下,使用rust构建HTTP服务器也是比较简单的,只需要简简单单的几行代码就可以搞定。

静态文件

显示静态文件也不是非常困难,我们在网页路径和本地的路径之间建立映射关系。

为了在网站的根目录也能显示网页,我们还需要单独处理一下get(“/”)这种情况,我们稍稍修改一下hello函数

use actix_files::{NamedFile};
#[get("/")]
async fn hello() -> Result<NamedFile> {Ok(NamedFile::open(PathBuf::from("./page/index.html"))?)
}

这样就可以显示静态页面了

统一处理错误


// response/result.rs
use actix_web::{error, HttpResponse, HttpResponseBuilder};
use actix_web::body::BoxBody;
use actix_web::http::{header, StatusCode};
use derive_more::{Display, Error};
use serde_json::json;#[derive(Display, Debug)]
pub enum YiBanResponseError {#[display(fmt = "格式化错误 :{}", _0)]FormatError(&'static str),#[display(fmt = "服务器错误 : {}", _0)]ServerError(&'static str),#[display(fmt = "数据库错误 : {}", _0)]DBError(&'static str),#[display(fmt = "未找到目标 : {}", _0)]NotFoundError(&'static str),
}impl error::ResponseError for YiBanResponseError {fn error_response(&self) -> HttpResponse {HttpResponseBuilder::new(self.status_code()).insert_header(("Content-Type", "application/json; charset=utf-8")).json(json!({"success":false,"message":self.to_string()}))}fn status_code(&self) -> StatusCode {match *self {YiBanResponseError::FormatError(_) => StatusCode::from_u16(510).unwrap(),YiBanResponseError::ServerError(_) => StatusCode::from_u16(511).unwrap(),YiBanResponseError::DBError(_) => StatusCode::from_u16(512).unwrap(),YiBanResponseError::NotFoundError(_) => StatusCode::from_u16(513).unwrap()}}
}

我们在需要返回错误的地方使用.map_err方法将错误映射为YibanResponseError,这将会相当方便,例如

#[get("/error")]
async fn return_error() -> Result<&'static str, YiBanResponseError> {Err(YiBanResponseError::ServerError("调试错误"))
}

连接MySQL数据库

这里需要使用sqlx,用法可以去看github的项目地址

连接

    let pool = MySqlPoolOptions::new().max_connections(50).connect(&format!("mysql://{}:{}@{}:{}/yiban","", "","","","")).await.unwrap_or_else(|_| { std::process::exit(exitcode::OK) });

放在appdata中

这是一个小技巧,我们把pool放在APPDATA中,放在这里面的数据是共享的,可以供每个router的handler函数调用,我们将在下面看到这会非常方便。

HttpServer::new(move || {App::new().app_data(web::Data::new(AppState {app_name: String::from("Actix-web"),pool: pool.clone(),})).service(hello).service(fs::Files::new("/", "./page").show_files_listing()).service(fs::Files::new("/js", "./page/js").show_files_listing()).service(fs::Files::new("/css", "./page/css").show_files_listing()).service(fs::Files::new("/img", "./page/img").show_files_listing())}).bind(("127.0.0.1", 8080))?.run().await

测试驱动开发

我们在函数上标注宏#[actix_web::test],就可以对这个函数进行测试。

向数据库插入数据

这里面我们主要使用了serde,sqlx,里面的一些宏使得我们可以给函数实现默认接口,这会非常方便。

// ./db_hander/user.rs
use actix_web::ResponseError;
use sqlx;
use serde::{Serialize, Deserialize};
use serde::de::Unexpected::Str;
use sqlx::mysql::MySqlPoolOptions;
use sqlx::{FromRow, MySql, Pool};#[derive(Serialize, Deserialize, Debug, FromRow)]
pub struct User {name: String,#[serde(rename(serialize = "phone_number", deserialize = "phone"))]#[sqlx(rename = "phone_number")]phone: String,data: String,password: String,// #[serde(default = "")]#[serde(skip_deserializing)]csrf: String,// #[serde(default = "")]#[serde(skip_deserializing)]phpsessid: String,
}impl User {pub fn new(name: &str, password: &str, phone: &str, data: &str) -> User {return User {name: String::from(name),password: String::from(password),csrf: "".to_string(),phone: String::from(phone),data: String::from(data),phpsessid: "".to_string(),};}pub async fn insert_data(self, pool: &Pool<MySql>) -> Result<(), sqlx::Error> {sqlx::query("insert into yiban.user(name,password,phone_number,data) values (?,?,?,?)").bind(self.name).bind(self.password).bind(self.phone).bind(self.data).execute(pool).await?;Ok(())}
}#[actix_web::test]
async fn insert_data_test() -> sqlx::Result<()> {println!("向数据库中插入数据的测试开始");let pool = MySqlPoolOptions::new().max_connections(50).connect("mysql://root:root@host:2222/user").await?;User::new("哈哈哈", "12345", "110", r#"{"aa":"bb"}"#).insert_data(&pool).await?;Ok(())
}

处理post请求

这里我们直接从data中拿到了pool进行数据库的操作

#[post("/fuck")]
async fn collect_info(mut payload: web::Payload, data: web::Data<AppState>) -> Result<HttpResponse, Error> {// payload is a stream of Bytes objectslet mut body = web::BytesMut::new();while let Some(chunk) = payload.next().await {let chunk = chunk?;// limit max size of in-memory payloadif (body.len() + chunk.len()) > MAX_SIZE {return Err(error::ErrorBadRequest("overflow"));}body.extend_from_slice(&chunk);}// body is loaded, now we can deserialize serde-jsonlet user = serde_json::from_slice::<User>(&body)?;let pool = &data.pool;user.insert_data(pool).await.map_err(|_| YiBanResponseError::DBError("数据已经存在或者格式不正确"))?;Ok(HttpResponse::Ok().json(json!({"success":true,"message":"提交成功"}))) // <- send response
}

配置文件

这里我们使用toml文件配置一些参数

// config_parser/parser.rsuse std::{fs, path};
use std::io::{Error, Read};
use actix_web::dev::Path;
use actix_web::web::to;
use serde::{Serialize, Deserialize};
use serde::de::Unexpected::Str;
use crate::YiBanResponseError;#[derive(Serialize, Deserialize, Debug)]
pub struct Config {pub(crate) dbconfig: DBConfig,appconfig: APPConfig,
}#[derive(Serialize, Deserialize, Debug)]
pub struct DBConfig {pub(crate) username: String,pub(crate) password: String,pub(crate) host: String,pub(crate) port: u16,
}#[derive(Serialize, Deserialize, Debug)]
pub struct APPConfig {app_name: String,
}pub async fn config(path: &str) -> Result<Config, actix_web::Error> {let mut file = fs::File::open(path).unwrap();let mut config_str = String::new();file.read_to_string(&mut config_str);let config: Config = toml::from_str(&config_str).map_err(|_| YiBanResponseError::ServerError("配置文件解析错误"))?;println!("{:#?}", config);Ok(config)
}#[actix_web::test]
async fn parse_config_test() {let mut file = fs::File::open("./Config.toml").unwrap();let mut config_str = String::new();file.read_to_string(&mut config_str);let config: Config = config("./Config.toml").await.unwrap();println!("{:#?}", config)
}

性能

以GET /js/chunk-vendors.cc18ca34.js测试性能

请求4000,并发数4000

actix-web性能:

gin性能:

请求4000,并发数1

actix-web
gin

Actix-Web构建一个简单的HTTP服务器相关推荐

  1. 通过python 构建一个简单的聊天服务器

    构建一个 Python 聊天服务器 一个简单的聊天服务器 现在您已经了解了 Python 中基本的网络 API:接下来可以在一个简单的应用程序中应用这些知识了.在本节中,将构建一个简单的聊天服务器.使 ...

  2. Nest的基本概念,以及如何使用Nest CLI来构建一个简单的Web应用程序

    Nest是一个用于构建高效.可扩展的Node.js服务器端应用程序的框架.它是基于Express.js构建的,并且提供了多种新特性和抽象层,可以让开发者更加轻松地构建复杂的应用程序. 本文将介绍Nes ...

  3. 如何构建一个简单的语音识别应用程序

    "In this 10-year time frame, I believe that we'll not only be using the keyboard and the mouse ...

  4. java jsf_使用Java和JSF构建一个简单的CRUD应用

    java jsf 使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护. 今天尝试Okta. JavaServer Fac ...

  5. 使用Java和JSF构建一个简单的CRUD应用

    使用Okta的身份管理平台轻松部署您的应用程序 使用Okta的API在几分钟之内即可对任何应用程序中的用户进行身份验证,管理和保护. 今天尝试Okta. JavaServer Faces(JSF)是用 ...

  6. python实现一个简单的http服务器

    需求:用python实现一个简单的http服务器 网页源码文件:https://download.csdn.net/download/d1240673769/46963534 该文件为html文件,如 ...

  7. rust服务器配置文件,使用Rust编写一个简单的Socket服务器(1):Rust下的配置载入...

    前言 早在2020年12月的时候,那会儿我正在看The Rust Programming Language.而这本书最后的"结业"任务是要编写一个简单的Socket服务器,而于此同 ...

  8. 使用libwebsockets搭建一个简单的websocket服务器

    本文讲解如何开发一个简单的WebSocket服务器 如果你嫌这两个例子都太简单了,且想了解更多更深的websocket的工作原理, 可以看这篇文章:http://lucumr.pocoo.org/20 ...

  9. 如何使用aframe.js构建一个简单的VR播放器

    在当今这个信息化的时代,虚拟现实(VR)已经开始逐渐成为一种新的生活方式.作为一名前端开发工程师,在学习和探索VR技术方面,aframe.js是一个非常有趣和有用的工具.在本文中,我将介绍如何使用af ...

最新文章

  1. linux mint 19 与windows时间不同步
  2. androidstudio返回之前界面_charles 如何修改服务器返回内容 - Breakpoints
  3. jdbc链接数据库mysql
  4. 113. Leetcode 674. 最长连续递增序列 (动态规划-子序列问题)
  5. 获取设备IMEI ,手机名称,系统SDK版本号,系统版本号
  6. rpm部署mysql_使用rpm快速安装部署MySQL5.6以及主从设置
  7. Android设计模式之——命令模式
  8. ios 倒数器_如何使用倒数计时器来停止游戏 – iOS [SWIFT] –
  9. [转载] Python字符串操作方法详解
  10. memcpy和strcpy的区别
  11. 图像的稀疏表示(Sparse Representation)
  12. Java架构师—PDMan数据库建模工具使用
  13. PyQt5最详细pyrcc5配置+样式使用
  14. android 转音频格式,android_Lame转换音频格式
  15. 【javaWeb微服务架构项目——乐优商城day14】——购物车(实现未登录状态的购物车,实现登陆状态下的购物车,实现未登录状态的购物车合并到登录状态)
  16. mod函数在vb中怎么用?
  17. POI 操作word
  18. uniapp实现滚动到底部加载更多数据
  19. 单身女生看过来:你为什么没有男朋友的20个原因
  20. 【Linux入门学习之】Ubuntu常用软件 速配指南之软件参考

热门文章

  1. 微型计算机原理含汇编语言课件,微型计算机原理第六章 汇编语言程序设计课件.ppt...
  2. 易优cms伪静态,EyouCms去除URL中的index.php
  3. 数据库中exists的用法
  4. 软件与中国古代史:政界往事(中)
  5. Oracle日志文件中状态为INVALID(原因分析)
  6. python 搜索引擎 实验楼的源码_Python语言之简历有错别字被拒绝聘用?文档被领导说?Python实现永无错别字!...
  7. 数字多道幅度分析器/APG7300
  8. 盛迈坤电商:提高产品吸引力的方法
  9. Bribe the Prisoners
  10. 早晨有感:让自己静下来