Skip to main content

framework_web_axum/
server.rs

1use crate::{AxumContext, AxumCors, AxumCorsFn, WebRouteWrapper, WebRouter, build_router_with};
2use anyhow::{Context, Result, anyhow};
3use framework_web::WebContext;
4use std::net::SocketAddr;
5use std::sync::{Arc, Mutex, OnceLock};
6use tokio::net::TcpListener;
7
8/// 创建 Axum 服务构造器,`bind_port` 小于等于 0 时使用随机端口。
9pub fn axum_builder(bind_address: impl Into<String>, bind_port: i32) -> AxumBuilder {
10    AxumBuilder {
11        bind_address: bind_address.into(),
12        bind_port: u16::try_from(bind_port).unwrap_or_default(),
13        cors: default_cors(),
14    }
15}
16
17/// Axum 服务构造器。
18pub struct AxumBuilder {
19    bind_address: String,
20    bind_port: u16,
21    cors: AxumCorsFn,
22}
23
24impl AxumBuilder {
25    /// 设置 CORS 解析函数,默认固定返回拒绝跨域的懒加载值。
26    pub fn with_cors<F>(mut self, cors: F) -> Self
27    where
28        F: Fn(&WebContext) -> AxumCors + Send + Sync + 'static,
29    {
30        self.cors = Arc::new(cors);
31        self
32    }
33
34    /// 绑定端口。
35    pub async fn bind(self) -> Result<AxumServer> {
36        let address = format!("{}:{}", self.bind_address, self.bind_port);
37        let listener = TcpListener::bind(&address)
38            .await
39            .with_context(|| format!("监听地址 {address} 失败"))?;
40        let local = listener
41            .local_addr()
42            .with_context(|| format!("读取监听地址 {address} 失败"))?;
43        Ok(AxumServer {
44            listener: Mutex::new(Some(listener)),
45            context: AxumContext::new(self.bind_address, local.port()),
46            cors: self.cors,
47            router: OnceLock::new(),
48        })
49    }
50}
51
52/// Axum 服务。
53pub struct AxumServer {
54    listener: Mutex<Option<TcpListener>>,
55    context: AxumContext,
56    cors: AxumCorsFn,
57    router: OnceLock<Arc<WebRouter>>,
58}
59
60impl AxumServer {
61    /// 绑定地址与实际端口。
62    pub fn context(&self) -> &AxumContext {
63        &self.context
64    }
65
66    /// 服务启动时创建的 Web 路由实例,未启动时为 `None`。
67    pub fn router(&self) -> Option<Arc<WebRouter>> {
68        self.router.get().cloned()
69    }
70
71    /// 启动服务并持续运行。
72    pub async fn run(&self, wrapper: Option<WebRouteWrapper>) -> Result<()> {
73        let listener = self
74            .listener
75            .lock()
76            .map_err(|error| anyhow!("Axum 监听器锁定失败:{error}"))?
77            .take()
78            .ok_or_else(|| anyhow!("Axum 服务已启动"))?;
79        let router = Arc::new(WebRouter::new(wrapper));
80        let app = build_router_with(
81            Arc::clone(&router),
82            self.context.clone(),
83            Arc::clone(&self.cors),
84        );
85        let _ = self.router.set(router);
86        axum::serve(
87            listener,
88            app.into_make_service_with_connect_info::<SocketAddr>(),
89        )
90        .await
91        .context("Axum 服务运行失败")
92    }
93}
94
95fn default_cors() -> AxumCorsFn {
96    Arc::new(|_| AxumCors::fixed().clone())
97}