Posted on :: Tags: , ,

起因

笔者有一个项目,由于用户的不断增加,需要对用户及相关信息做一个后台管理软件。由于笔者之前技术栈是linux后端开发,实话说对web应用一窍不通😅,因此,借此机会学习一下。

web应用

一个合格的管理后台,本质上就是一个web应用(通过浏览器来实现功能的应用)。

web应用根据技术不同可以分为以下几种实现方式:

技术模式适合的应用类型是否需要搜索引擎优化内容更新频率开发复杂度
SPA单页应用管理后台、Web 工具❌ 否⭐⭐⭐
MPA多页应用内容展示、新闻类网站✅ 是
SSR服务端渲染电商首页、SEO 页✅ 是⭐⭐⭐⭐
SSG静态站点生成博客、官网、文档✅ 是⭐⭐
ISR渐进式静态生成海量内容平台✅ 是中高⭐⭐⭐⭐⭐

web应用的实现方式

对于绝大部分web应用来说,不管是SPA还是其他,都需要前端+后端来实现功能。

前端

前端: 负责页面展示、用户交互、与后端的数据通信等。通常使用 HTML、CSS、JavaScript 或其他现代前端框架(如 Vue.js、React、Angular)来构建。

后端

后端: 负责业务逻辑、数据库操作、数据处理等。可以使用 Node.js、Django、Flask、Java、Ruby on Rails 等技术栈来开发后端应用。

例如,前端使用HTML+JS实现一些基本功能,后端使用go编写的http服务端。

func main() {
    http.HandleFunc("/login", func(w http.ResponseWriter, r *http.Request) {
		tmpl, err := template.ParseFiles("../html/login.html")
		if err != nil {
			http.Error(w, err.Error(), http.StatusInternalServerError)
			return
		}

		tmpl.Execute(w, nil)
	})

    ...

    fs := http.FileServer(http.Dir("../html"))
	http.Handle("/static/", http.StripPrefix("/static/", fs))
	http.Handle("/", fs)

	http.ListenAndServe(":8082", nil)
}

当时并没有对web开发有一个整体上的认识,因此开发时借助AI快速开发完成。以为 Web 开发不过如此,实以浅尝辄止,坐井观天😵。

框架选型

使用框架,可以提供一些常见的功能,避免重复造轮子;另外,许多框架内置了一些常见的安全防护措施(如防止 XSS、CSRF 攻击等),大大减少了开发者在实现安全功能时的压力。目前有很多优秀的前后端框架,例如我们听过的Node.js,vue等都是主流的框架。

Node.js主要用于构建高性能的后端 HTTP服务器,常用于构建 RESTful APIs 和 GraphQL APIs,作为前后端通信的中间层,特别是在需要处理大量并发请求和实时交互的场景下非常高效。

Vue.js主要功能是帮助开发者构建交互丰富的前端界面,提供高效的数据绑定和组件化开发,是一款非常常用的前端框架。

针对管理后台来说,github上后很多优秀稳定的框架,实现了大部分功能。

前端选用SoybeanAdmin:https://github.com/soybeanjs/soybean-admin

后端采用rust开发的Axum:https://github.com/tokio-rs/axum

SoybeanAdmin

SoybeanAdmin是基于vue3开发的,因此,需要先安装依赖,包括nodejs和visio studio等。

SoybeanAdmin和后端通信的接口已经定义完成,开发者需要找到这个定义然后根据结构,在后端设置对应的返回就可以。

路由管理

SoybeanAdmin支持静态路由和动态路由。

设置静态路由时,路由表是写死的,前端打包的时候就定好了有哪些页面。设置constant: true的页面不登陆就能访问,其他页面登录之后全部可以访问。

而动态路由模式下,前端页面启动后会向后端请求路由表,根据用户权限动态加载哪些页面可以访问。后端将路由分为公共路由和权限路由两部分,公共路由无需登录,比如 /login、/404,这些放在 fetchGetConstantRoutes 接口里,由后端返回;需要登录的路由放在fetchGetUserRoutes 接口里,登录成功后根据角色动态返回。

对于角色控制不太严格的功能,可以使用静态路由。

Axum

Axum 是一个用 Rust语言 写的,专门用来构建**高性能 Web 服务(API服务器)**的框架。由 Tokio 团队(Rust里著名的异步运行时 Tokio)开发和维护。

针对Rust语言的内存安全以及高并发特性,我们可以单独出一篇文章来和c++做一个对比。

目录设置

  1. cargo new aumx新建一个项目
  2. 添加依赖
  3. 构建目录,可以适当简化
src/
├── main.rs              // 启动文件,程序入口
├── api/
│   ├── mod.rs           // API 路由总入口
│   ├── health_check.rs  // 健康检查路由
│   ├── target.rs        // 目标相关的 API
│   └── user.rs          // 用户相关的 API
├── handlers/
│   ├── mod.rs           // 处理程序入口
│   ├── target_handler.rs // 处理目标相关逻辑
│   └── user_handler.rs   // 处理用户相关逻辑
├── models/
│   ├── mod.rs           // 数据模型入口
│   ├── target.rs        // 目标模型
│   └── user.rs          // 用户模型
├── database/
│   ├── mod.rs           // 数据库配置和连接池初始化
│   ├── mysql.rs         // MySQL 特定的操作(可选)
│   └── migrations.rs    // 数据库迁移脚本(可选)
├── middlewares/
│   ├── mod.rs           // 中间件入口
│   ├── auth.rs          // 认证中间件
│   └── logging.rs       // 日志中间件
└── config/
    ├── mod.rs           // 配置文件读取和初始化
    └── app_config.rs    // 应用相关配置(如端口、环境变量等)

代码编写

对于大部分简单web后端,其主要就是一个http服务器,接收客户端发送的请求,进行路由转发以及逻辑处理。

对于大型的web后端,还需要中间件支持,状态管理与会话控制,负载均衡与高可用性,高并发支持等。

Axum 请求处理

#[tokio::main]
async fn main() {
    config::load_config();

    // 启动服务器,监听 8080 端口
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000")
    .await
    .unwrap();
    println!("listening on {}", listener.local_addr().unwrap());
    axum::serve(listener, app).await.unwrap();
}

简简单单一句话,即可实现http服务器

当然还可以设置一些数据库连接,配置文件获取等

// 获取数据库连接 URL
let db_url = env::var("DATABASE_URL").expect("DATABASE_URL must be set");
println!("Database URL: {}", db_url);

// 初始化数据库连接池
let pool = database::database_init(&db_url).await;

// 将连接池包装成 Arc 以便共享
let shared_pool = Arc::new(pool);

路由设置

创建路由:

// 创建路由并传入连接池
let app = api::create_routes(shared_pool);

由于需要连接数据库,因此需要把数据库连接池绑定到路由对象

pub fn create_routes(pool: Arc<sqlx::MySqlPool>) -> Router {
	Router::new()
	.route("/api/login", post(user::handle_login))
	.route("/api/home", get(user::handle_home))
}

路由转发也比较简单,依次填入路由地址,请求方法和处理方法即可

数据处理

对于不同的请求方法,使用不同的传入结构体,例如,post请求需要传入JSON结构体,get请求需要传入Query结果体

pub async fn handle_login( 
    State(config): State<Arc<AppConfig>>,
    Json(payload): Json<LoginPayload>,
) -> Result<Json<ApiResponse<LoginToken>>, StatusCode> {
	...
}

整体来说,Axum很适合小白,笔者用了两天就搭建起了一个web后台服务,很多设置都是直接填入即可。

nginx

如果部署到云端,不要忘记最后设置nginx。

前端向后端服务或者数据库请求时,分为两种情况:

  1. 连接到80http或者443https先到nginx,然后由nginx进行内部转发。外部客户端只能看到Nginx的端口

  2. http或https直接连接到后端,例如后端http(不安全)或https服务器制定了/api/select这个路由,此时如果后端服务的端口开放,则可以直接进行请求。

第一种更安全,nginx反向代理不会暴露端口。

Copyright 2025 wakamda 冀ICP备2025102883号-1