静态资源中间件
2025/11/3大约 1 分钟hyperlanewebrustmiddlewarestatic-file
静态资源中间件
原生写法
use hyperlane::*;
const STATIC_DIR_PATH_KEY: &str = "static_dir_path";
const STATIC_FILE_DATA_KEY: &str = "static_file_data";
struct HttpVersionMiddleware;
struct StaticMiddleware;
struct ResponseMiddleware;
impl ServerHook for HttpVersionMiddleware {
    async fn new(_ctx: &Context) -> Self {
        Self
    }
    async fn handle(self, ctx: &Context) {
        ctx.set_response_version(HttpVersion::HTTP1_1)
            .await
            .set_attribute(STATIC_DIR_PATH_KEY, "./")
            .await;
    }
}
impl ServerHook for StaticMiddleware {
    async fn new(_ctx: &Context) -> Self {
        Self
    }
    async fn handle(self, ctx: &Context) {
        let static_path_opt: Option<&str> = ctx.try_get_attribute(STATIC_DIR_PATH_KEY).await;
        let static_path: &str =
            static_path_opt.expect(&format!("attribute {STATIC_DIR_PATH_KEY} not found"));
        let path: String = ctx.get_request_path().await;
        let file_path: String = format!("{static_path}{path}");
        let file_extension: String = FileExtension::get_extension_name(&file_path);
        let content_type: &'static str = FileExtension::parse(&file_extension).get_content_type();
        let content_type: String =
            ContentType::format_content_type_with_charset(content_type, UTF8);
        ctx.set_response_header(CONTENT_TYPE, content_type).await;
        let file_data_opt: Option<String> = tokio::fs::read_to_string(&file_path).await.ok();
        ctx.set_attribute(STATIC_FILE_DATA_KEY, file_data_opt).await;
    }
}
impl ServerHook for ResponseMiddleware {
    async fn new(_ctx: &Context) -> Self {
        Self
    }
    async fn handle(self, ctx: &Context) {
        let static_file_data_opt: Option<String> = ctx
            .try_get_attribute(STATIC_FILE_DATA_KEY)
            .await
            .expect("attribute static_file_data not found");
        let _ = ctx
            .set_response_body(static_file_data_opt.unwrap_or_default())
            .await
            .send()
            .await;
    }
}
#[tokio::main]
async fn main() {
    Server::new()
        .await
        .request_middleware::<HttpVersionMiddleware>()
        .await
        .request_middleware::<StaticMiddleware>()
        .await
        .response_middleware::<ResponseMiddleware>()
        .await
        .run()
        .await
        .unwrap()
        .wait()
        .await
}宏写法
use hyperlane::*;
use hyperlane_utils::*;
const STATIC_DIR_PATH_KEY: &str = "static_dir_path";
const STATIC_FILE_DATA_KEY: &str = "static_file_data";
#[request_middleware(1)]
struct HttpVersionMiddleware;
#[request_middleware(2)]
struct StaticMiddleware;
#[response_middleware(1)]
struct ResponseMiddleware;
impl ServerHook for HttpVersionMiddleware {
    async fn new(_ctx: &Context) -> Self {
        Self
    }
    #[response_version(HttpVersion::HTTP1_1)]
    async fn handle(self, ctx: &Context) {
        ctx.set_attribute(STATIC_DIR_PATH_KEY, "./").await;
    }
}
impl ServerHook for StaticMiddleware {
    async fn new(_ctx: &Context) -> Self {
        Self
    }
    #[prologue_macros(request_path(path), attribute(STATIC_DIR_PATH_KEY => static_path_opt: &str))]
    #[epilogue_macros(response_header(CONTENT_TYPE => content_type))]
    async fn handle(self, ctx: &Context) {
        let static_path: &str =
            static_path_opt.expect(&format!("attribute {STATIC_DIR_PATH_KEY} not found"));
        let file_path: String = format!("{static_path}{path}");
        let file_extension: String = FileExtension::get_extension_name(&file_path);
        let content_type: &'static str = FileExtension::parse(&file_extension).get_content_type();
        let content_type: String =
            ContentType::format_content_type_with_charset(content_type, UTF8);
        let file_data_opt: Option<String> = tokio::fs::read_to_string(&file_path).await.ok();
        ctx.set_attribute(STATIC_FILE_DATA_KEY, file_data_opt).await;
    }
}
impl ServerHook for ResponseMiddleware {
    async fn new(_ctx: &Context) -> Self {
        Self
    }
    #[attribute(STATIC_FILE_DATA_KEY => static_file_data_opt: String)]
    async fn handle(self, ctx: &Context) {
        let static_file_data: String =
            static_file_data_opt.expect("attribute static_file_data not found");
        let _ = ctx.set_response_body(static_file_data).await.send().await;
    }
}
#[tokio::main]
async fn main() {
    Server::new().await.run().await.unwrap().wait().await
}