请求与响应深入
每个 HTTP 服务器的核心都在于处理传入的请求和生成传出的响应。Hyperlane 为这两个方面提供了全面且符合人体工程学的 API。本文将深入探讨 hyperlane 如何处理请求和响应,涵盖从基本头访问到 JSON 解析、Cookie 以及属性宏的所有内容。
理解请求生命周期
当客户端向 hyperlane 服务器发送 HTTP 请求时,会发生以下序列:
- 接受 TCP 连接并创建
Stream。 - 将请求从流解析为结构化的
Request对象。 - 请求通过所有已注册的请求中间件。
- 请求与已注册的路由进行匹配。
- 调用匹配路由的
handle方法。 - 响应通过所有已注册的响应中间件。
- 响应被序列化并发送回客户端。
理解这个生命周期对于编写有效的中间件和路由处理程序至关重要。
访问请求数据
Hyperlane 提供了多种访问请求数据的方式 —— 通过方法调用和通过属性宏。
基于方法的访问
Context 对象让你可以访问传入请求的所有方面:
let method = ctx.get_request().get_method();
let path = ctx.get_request().get_path();
let host = ctx.get_request().get_host();
let headers = ctx.get_request().get_headers();
let body = ctx.get_request().get_body_string();
let json_body: T = ctx.get_request().get_body_json::<T>();
let query = ctx.get_request().try_get_query("key");
让我们逐一了解:
get_method()—— 返回 HTTP 方法(GET、POST、PUT、DELETE 等)。get_path()—— 返回请求的 URL 路径部分。get_host()—— 返回 Host 头的值。get_headers()—— 返回所有请求头。get_body_string()—— 返回原始请求体字符串。get_body_json::<T>()—— 将请求体作为 JSON 反序列化为类型T。这要求T实现serde::Deserialize。try_get_query("key")—— 尝试按名称提取查询参数。
属性宏访问
Hyperlane 还提供了属性宏,可以自动提取请求数据并将其绑定到变量:
#[request_body(body)]
#[request_body_json(body: TestData)]
#[request_header(HOST => host_value)]
#[try_get_request_header(HOST => host_value)]
#[request_path(path)]
#[request_query("key" => query_value)]
#[is_get_method]
#[methods("GET", "POST")]
#[is_http1_1_version]
#[is_ws_upgrade_type]
这些宏应用于路由处理程序的 handle 方法。例如:
#[route("/test/{text}")]
struct Route;
impl ServerHook for Route {
async fn new(_: &mut Stream, _: &mut Context) -> Self { Self }
#[request_body_json(body: TestData)]
#[request_query("key" => query_value)]
async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
// `body` 现在是从请求体反序列化的 TestData 实例
// `query_value` 是 "key" 查询参数的值
Status::Continue
}
}
属性宏支持:
#[request_body(body)]—— 将原始请求体字符串绑定到body。#[request_body_json(body: TestData)]—— 将 JSON 体反序列化为TestData结构体。#[request_header(HOST => host_value)]—— 将 HOST 头提取到host_value中。#[try_get_request_header(HOST => host_value)]—— 安全地尝试提取头。#[request_path(path)]—— 将请求路径绑定到path。#[request_query("key" => query_value)]—— 提取查询参数。#[is_get_method]—— 检查请求方法是否为 GET。#[methods("GET", "POST")]—— 检查请求方法是否匹配指定的方法之一。#[is_http1_1_version]—— 检查请求是否使用 HTTP/1.1。#[is_ws_upgrade_type]—— 检查请求是否为 WebSocket 升级请求。
路由参数
动态路由参数是任何 Web 框架的核心特性。Hyperlane 在路由路径中使用 {param_name} 语法:
server.route::<Route>("/test/{text}");
server.route::<Route>("/test/{number:\\d+}");
路由参数可以通过 Context 访问:
let param: Option<String> = ctx.try_get_route_param("text");
let param: String = ctx.get_route_param("text");
let params = ctx.get_route_params();
try_get_route_param("text")—— 返回Option<String>,对可选参数安全。get_route_param("text")—— 返回String,如果参数不存在会 panic。get_route_params()—— 返回所有路由参数的集合。
对于正则表达式约束的参数,如 {number:\\d+},仅当 URL 段满足正则表达式模式时,路由才会匹配。
路由过滤器
Hyperlane 通过属性宏支持路由级别的过滤:
#[host("example.com")]
#[reject_host("blocked.com")]
#[referer("https://example.com")]
#[reject_referer("https://malicious.com")]
#[filter(ctx.get_request().get_method() == &RequestMethod::Get)]
#[reject(ctx.get_request().get_path().len() > 1000)]
这些过滤器允许你:
#[host("example.com")]—— 仅匹配到特定主机的请求。#[reject_host("blocked.com")]—— 拒绝到特定主机的请求。#[referer("https://example.com")]—— 仅匹配来自特定来源的请求。#[reject_referer("https://malicious.com")]—— 拒绝来自特定来源的请求。#[filter(...)]—— 应用自定义过滤表达式。#[reject(...)]—— 应用自定义拒绝表达式。
构建响应
Hyperlane 的 Context 提供了对响应对象的可变访问:
ctx.get_mut_response().set_version(HttpVersion::Http1_1).set_status_code(200);
ctx.get_mut_response().set_body("Hello World");
ctx.get_mut_response().set_header(CONTENT_TYPE, APPLICATION_JSON);
ctx.get_mut_response().add_header(SERVER, "hyperlane");
关键响应方法:
set_version(HttpVersion::Http1_1)—— 设置 HTTP 版本。set_status_code(200)—— 设置 HTTP 状态码。set_body("Hello World")—— 设置响应体。set_header(CONTENT_TYPE, APPLICATION_JSON)—— 设置响应头(替换现有值)。add_header(SERVER, "hyperlane")—— 添加响应头(允许多个值)。
属性宏响应
你也可以使用属性宏来配置响应:
#[response_status_code(200)]
#[response_version(HttpVersion::Http1_1)]
#[response_body("Hello World")]
#[response_header(SERVER => HYPERLANE)]
#[response_header(SET_COOKIE, "session_id=abc123")]
#[clear_response_headers]
这些宏应用于 handle 方法:
#[response_status_code(200)]
#[response_body("Hello World")]
async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
// 响应自动配置了状态码 200 和体 "Hello World"
Status::Continue
}
使用 Cookie
Cookie 对于会话管理和有状态 Web 应用至关重要。Hyperlane 提供了全面的 Cookie 支持:
读取 Cookie
let cookies = ctx.get_request().try_get_cookies();
let cookie = ctx.get_request().try_get_cookie("session_id");
try_get_cookies()—— 返回请求中的所有 Cookie。try_get_cookie("session_id")—— 按名称返回特定的 Cookie。
使用 CookieBuilder 设置 Cookie
Hyperlane 提供了 CookieBuilder 来构建具有各种属性的 Cookie:
let cookie = CookieBuilder::new("session_id", "abc123").set_path("/").http_only().build();
ctx.get_mut_response().set_header(SET_COOKIE, &cookie);
CookieBuilder 支持:
let cookie = CookieBuilder::new("session", "token123")
.set_expires("Wed, 21 Oct 2025 07:28:00 GMT")
.set_domain("example.com")
.set_same_site("Strict")
.set_max_age(3600)
.set_path("/")
.secure()
.http_only()
.build();
可用的构建器方法:
set_expires(...)—— 设置 Cookie 过期日期。set_domain(...)—— 设置 Cookie 域。set_same_site(...)—— 设置 SameSite 属性(Strict、Lax、None)。set_max_age(...)—— 设置 Cookie 最大存活时间(秒)。set_path(...)—— 设置 Cookie 路径。secure()—— 将 Cookie 标记为 Secure(仅 HTTPS)。http_only()—— 将 Cookie 标记为 HttpOnly(JavaScript 无法访问)。
清除 Cookie
要清除 Cookie,创建一个值为空且 max age 为 0 的 Cookie:
let clear_cookie = CookieBuilder::new("session", "").set_max_age(0).build();
发送响应
构建响应后,你需要将其发送给客户端:
let data = ctx.get_mut_response().build();
stream.try_send(data).await;
stream.send(data).await;
stream.try_send_list(&frame_list).await;
stream.try_flush().await;
build()—— 将响应序列化为字节。stream.try_send(data).await—— 尝试发送数据,如果流已关闭则返回错误。stream.send(data).await—— 发送数据,阻塞直到完成。stream.try_send_list(&frame_list).await—— 一次发送多个帧。stream.try_flush().await—— 刷新所有缓冲数据。
发送属性宏
#[try_send]
#[send]
#[try_flush]
#[flush]
#[closed]
这些宏自动处理发送/刷新操作:
#[try_send]
async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
// 响应自动构建并发送
Status::Continue
}
常见模式
JSON API 响应
ctx.get_mut_response()
.set_header(CONTENT_TYPE, APPLICATION_JSON)
.set_body(json_string);
let data = ctx.get_mut_response().build();
stream.try_send(data).await;
身份验证中间件
impl ServerHook for AuthMiddleware {
async fn handle(self, stream: &mut Stream, ctx: &mut Context) -> Status {
let auth_str = ctx.get_request().try_get_header_back(AUTHORIZATION).unwrap_or_default();
if auth_str.is_empty() {
let data = ctx.get_mut_response().set_status_code(401).set_body("Unauthorized").build();
if stream.try_send(data).await.is_err() {
stream.set_closed(true);
}
return Status::Reject;
}
Status::Continue
}
}
CORS 中间件
ctx.get_mut_response()
.set_header(ACCESS_CONTROL_ALLOW_ORIGIN, WILDCARD_ANY)
.set_header(ACCESS_CONTROL_ALLOW_METHODS, ALL_METHODS)
.set_header(ACCESS_CONTROL_ALLOW_HEADERS, WILDCARD_ANY);
总结
Hyperlane 为处理请求和响应提供了丰富的双模式 API。无论你更喜欢显式的方法调用风格还是简洁的属性宏风格,你都可以完全控制 HTTP 请求/响应周期的每个方面。路由参数、过滤器、Cookie 管理和灵活的响应构建的组合,使 hyperlane 成为构建任何复杂度的 Web 应用的强大工具。