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

Rust Axum实现Controller合并路由遇404,疑路由实例变更问题

问题:Axum Controller模式下路由返回404错误

我用Rust和Axum开发简单REST API,尝试将路由、HTTP请求方法与处理器整合,模拟其他语言框架的Controller功能。启动服务器后访问/projects(端口9090)时收到404错误,而非预期的项目列表或空列表,怀疑问题出在路由实例的变更方式上。

核心代码

Controller定义及初始化代码

use crate::state::AppState;
use axum::response::IntoResponse;
use axum::Router;
use serde::{Deserialize, Serialize};
mod create;
mod delete;
mod find_one;
mod health_check;
mod list;
mod update;

const HEALTH_CHECK_PATH: &str = "/health_check";
const PROJECTS_PATH: &str = "/projects";
const PROJECTS_NAME_PATH: &str = "/projects/:name";

#[derive(Clone, Debug)]
pub struct Controller {
    pub(crate) router: Router<AppState>,
}

impl Controller {
    pub fn new() -> Controller {
        let controller = Controller {
            router: Router::new(),
        };

        controller
            .create()
            .delete()
            .find_one()
            .health_check()
            .list()
            .update();

        controller
    }
}

list()方法代码

use crate::state::AppState;
use axum::extract::{Query, State};
use axum::http::StatusCode;
use axum::response::IntoResponse;
use axum::routing::get;
use axum::Json;
use axum_macros::debug_handler;
use serde::Deserialize;
use crate::controller::{Controller, PROJECTS_PATH};

impl Controller {
    pub fn list(&self) -> &Controller {
        // #[derive(Debug, Deserialize, Default)]
        // pub struct Pagination {
        //     pub offset: Option<usize>,
        //     pub limit: Option<usize>,
        // }

        #[debug_handler]
        pub async fn handler(
            //pagination: Option<Query<Pagination>>,
            State(state): State<AppState>,
        ) -> impl IntoResponse {
            //let Query(_pagination) = pagination.unwrap_or_default();
            match state.query_port.read().unwrap().list() {
                Ok(res) => (StatusCode::OK, Json(res)).into_response(),
                Err(err) => {
                    (StatusCode::INTERNAL_SERVER_ERROR, Json(err.to_string())).into_response()
                }
            }
        }

        let _ = self.router.clone().route(PROJECTS_PATH, get(handler));
        self
    }
}

问题根源

  1. 初始化时未修改原Controller的Router:Controller::new()中创建初始controller后,链式调用的.create()、.list()等方法返回的是原实例的引用,但这些方法并未修改原实例的router属性,最后返回的还是初始的空Router。

  2. 路由修改仅作用于克隆实例:在list()方法中,self.router.clone().route(...)只是对Router的副本添加路由,原Controller的router完全没有被更新,相当于白执行了添加路由的操作。

修复方案

步骤1:修改Controller方法为可变引用,更新内部Router

将所有Controller的方法(如list、create等)改为接收&mut self,并将self.router替换为添加路由后的新Router实例(Axum的Router是不可变的,每次调用.route()都会生成新实例):

以list()方法为例:

impl Controller {
    pub fn list(&mut self) -> &mut Self {
        // 保留handler代码不变
        #[debug_handler]
        pub async fn handler(
            State(state): State<AppState>,
        ) -> impl IntoResponse {
            match state.query_port.read().unwrap().list() {
                Ok(res) => (StatusCode::OK, Json(res)).into_response(),
                Err(err) => {
                    (StatusCode::INTERNAL_SERVER_ERROR, Json(err.to_string())).into_response()
                }
            }
        }

        // 直接更新self.router为添加路由后的新实例
        self.router = self.router.route(PROJECTS_PATH, get(handler));
        self
    }
}

步骤2:修改Controller::new()使用可变实例

初始化时使用可变的Controller,确保链式调用的方法能修改内部的Router:

impl Controller {
    pub fn new() -> Self {
        let mut controller = Controller {
            router: Router::new(),
        };

        // 链式调用可变方法,修改controller的router
        controller
            .create()
            .delete()
            .find_one()
            .health_check()
            .list()
            .update();

        controller
    }
}

步骤3:统一其他方法的修改逻辑

对create、delete、find_one等其他方法执行同样的修改:将方法签名改为pub fn xxx(&mut self) -> &mut Self,并在方法内用self.router = self.router.route(...)更新路由。

这样修改后,Controller的router会正确包含所有注册的路由,访问/projects时就能正确触发对应的处理器,不会再返回404。

内容的提问来源于stack exchange,提问作者A Bit of Help

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 23:45:14