You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用axum与rust-embed托管并嵌入SPA文件及路由问题

解决方案:使用rust-embed + axum 托管嵌入式SPA

依赖配置

首先在Cargo.toml中添加所需依赖:

[dependencies]
axum = "0.7"
rust-embed = "8.0"
tokio = { version = "1.0", features = ["full"] }
mime_guess = "2.0"

嵌入静态文件

用rust-embed的宏指定SPA静态文件目录,编译时文件会被嵌入可执行文件:

use rust_embed::RustEmbed;

#[derive(RustEmbed)]
#[folder = "./app/static/"] // 替换为你的SPA静态文件实际路径
struct SpaAssets;

自定义SPA请求处理逻辑

编写统一的请求处理器,解决路由冲突和嵌套路由问题:

use axum::{
    http::{StatusCode, HeaderValue},
    response::{IntoResponse, Response},
};
use mime_guess::from_path;
use std::path::PathBuf;

async fn serve_spa(path: Option<PathBuf>) -> Response {
    // 默认请求根路径时返回index.html
    let target_path = path.unwrap_or_else(|| PathBuf::from("index.html"));

    // 尝试获取嵌入的文件
    match SpaAssets::get(target_path.to_str().unwrap_or("index.html")) {
        Some(asset) => {
            // 根据文件后缀推断MIME类型,确保浏览器正确解析资源
            let mime_type = from_path(&target_path)
                .first_or_octet_stream()
                .to_string();
            let headers = [(
                axum::http::header::CONTENT_TYPE,
                HeaderValue::from_str(&mime_type).unwrap(),
            )];
            (headers, asset.data).into_response()
        }
        None => {
            // 找不到对应文件时返回index.html,交由前端路由处理嵌套路径
            match SpaAssets::get("index.html") {
                Some(index_asset) => {
                    let headers = [(
                        axum::http::header::CONTENT_TYPE,
                        HeaderValue::from_static("text/html; charset=utf-8"),
                    )];
                    (StatusCode::OK, headers, index_asset.data).into_response()
                }
                None => (StatusCode::NOT_FOUND, "Missing index.html").into_response()
            }
        }
    }
}

配置Axum路由

将所有GET请求导向自定义处理器,同时可以优先添加API路由避免被SPA逻辑拦截:

#[tokio::main]
async fn main() {
    let app = axum::Router::new()
        // 先注册API路由(如果有),确保优先匹配
        // .route("/api/user", axum::routing::get(get_user))
        // 匹配所有路径,处理SPA资源和嵌套路由
        .route("/*path", axum::routing::get(serve_spa));

    let addr = ([127, 0, 0, 1], 3000).into();
    println!("Server running on http://{}", addr);
    axum::Server::bind(&addr)
        .serve(app.into_make_service())
        .await
        .unwrap();
}

方案说明

  1. 解决嵌套路由404问题:当用户刷新/home/customers这类虚拟路由时,处理器找不到对应文件,自动返回index.html,前端路由会接管路径渲染对应页面。
  2. 解决路由冲突问题:不再单独注册/index.html路由,所有请求通过统一处理器判断文件是否存在,避免了路由冲突 panic。
  3. 嵌入文件到可执行文件:rust-embed在编译阶段将静态文件打包进可执行文件,部署时无需携带额外静态资源目录。

注意事项

  • 确保SPA资源的引用路径为根路径(如/app.js而非./app.js),否则嵌入后无法正确加载。
  • API路由必须放在/*path路由之前,Axum会按路由定义顺序匹配,避免API请求被SPA处理器拦截。
  • 静态文件修改后需要重新编译,因为rust-embed是编译时嵌入文件。

内容的提问来源于stack exchange,提问作者Fred Hors

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 17:18:18