静态资源中间件
2026/2/27大约 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: &mut Context) -> Self {
Self
}
async fn handle(self, ctx: &mut Context) {
ctx.get_mut_response().set_version(HttpVersion::Http1_1);
ctx.set_attribute(STATIC_DIR_PATH_KEY, "./");
}
}
impl ServerHook for StaticMiddleware {
async fn new(_ctx: &mut Context) -> Self {
Self
}
async fn handle(self, ctx: &mut Context) {
let static_path_opt: Option<&str> = ctx.try_get_attribute(STATIC_DIR_PATH_KEY);
let static_path: &str =
static_path_opt.expect(&format!("attribute {STATIC_DIR_PATH_KEY} not found"));
let path: &String = ctx.get_request().get_path();
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.get_mut_response()
.set_header(CONTENT_TYPE, content_type);
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);
}
}
impl ServerHook for ResponseMiddleware {
async fn new(_ctx: &mut Context) -> Self {
Self
}
async fn handle(self, ctx: &mut Context) {
let static_file_data_opt: Option<String> = ctx
.try_get_attribute(STATIC_FILE_DATA_KEY)
.expect("attribute static_file_data not found");
ctx.get_mut_response()
.set_body(static_file_data_opt.unwrap_or_default());
if ctx.try_send().await.is_err() {
ctx.set_aborted(true).set_closed(true);
}
}
}
#[tokio::main]
async fn main() {
Server::default()
.request_middleware::<HttpVersionMiddleware>()
.request_middleware::<StaticMiddleware>()
.response_middleware::<ResponseMiddleware>()
.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: &mut Context) -> Self {
Self
}
#[response_version(HttpVersion::Http1_1)]
async fn handle(self, ctx: &mut Context) {
ctx.set_attribute(STATIC_DIR_PATH_KEY, "./");
}
}
impl ServerHook for StaticMiddleware {
async fn new(_ctx: &mut Context) -> Self {
Self
}
#[prologue_macros(request_path(path), attribute_option(STATIC_DIR_PATH_KEY => static_path_opt: &str))]
#[epilogue_macros(response_header(CONTENT_TYPE => content_type))]
async fn handle(self, ctx: &mut 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);
}
}
impl ServerHook for ResponseMiddleware {
async fn new(_ctx: &mut Context) -> Self {
Self
}
#[attribute_option(STATIC_FILE_DATA_KEY => static_file_data_opt: String)]
async fn handle(self, ctx: &mut Context) {
let static_file_data: String =
static_file_data_opt.expect("attribute static_file_data not found");
ctx.get_mut_response().set_body(static_file_data);
if ctx.try_send().await.is_err() {
ctx.set_aborted(true).set_closed(true);
}
}
}
#[tokio::main]
async fn main() {
Server::default().run().await.unwrap().wait().await
}