原文地址
https://rustasync.github.io/team/2018/11/07/tide-middleware.html
在Tide中的路由和提取提议得到积极响应之后,我很高兴地说,GitHub上提供了一个初始实现!作为Rust的生态系统提供的强大基础的证明,基本框架实现只需要大约1000行代码。
The repository 中充满了许多问题,其中相当多的问题被标记为良好的第一个问题。在这一点上,Tide已经具备了足够的框架,可以将持续的开发转变为完全协作的工作。这是一个很好的时间来参与并塑造最终将成为最初0.1版本的东西!
作为这个初始实现的一部分,我还提供了一个简单的中间件设计,以及一个计算值的新概念。本文的其余部分将介绍这两个新增内容。
Middleware (中间件)
提出的中间件方法非常简单,直接来自actix-web:
pub trait Middleware<Data>: Send + Sync {
/// Asynchronously transform the incoming request, or abort further handling by immediately
/// returning a response.
fn request(
&self,
data: &mut Data,
req: Request,
params: &RouteMatch<'_>,
) -> FutureObj<'static, Result<Request, Response>>;
/// Asynchronously transform the outgoing response.
fn response(
&self,
data: &mut Data,
head: &Head,
resp: Response,
) -> FutureObj<'static, Response>;
}
在本设计中,中间件可以:
- 在继续到下一个中间件或端点之前更改请求数据
- 在端点处理之前或之后执行副作用
- 通过直接生成响应中止进一步的处理
- 在输出时转换响应
此功能是通过异步函数提供的。FutureObj在这里的使用反映了目前 boxed futures 的表达方式,预计在不久的将来会变成Box<Future>。虽然boxing the futures 有一些性能成本,但预计成本非常小,而且装箱允许我们避免更复杂的类型跟踪(以及相关的长编译时间)。这种技术已经在actix-web等框架中得到了验证。
注意,请求类型只是来自http crate的一个,它包含一个扩展类型映射,中间件可以使用该扩展类型映射将任意信息通信到端点或其方法之间。
如果您熟悉actix-web,您可能会注意到没有对应于finish的方法,即在响应传输之后运行的方法。有一个有待解释的问题;请随意试一试!
中间件以类似的简单方式添加到应用程序中:
impl<Data> App<Data> {
pub fn middleware(&mut self, middleware: impl Middleware<Data> + 'static) -> &mut Self { ... }
}
最终,我们希望支持更细粒度的中间件应用程序,例如只将其应用于特定的一组子程序。这也是一个问题。
计算值 (Computed values)
虽然上面的中间件故事简单而灵活,但它常常是多余的。Tide中的一个新概念是计算值,即可以根据请求按需生成的值。例如,您经常希望将URL的查询部分解析为组件。端点或各种中间件可能需要这种解析。我们不需要编写中间件来执行解析并将其保存在扩展中(这需要仔细地对中间件进行排序,并处理请求状态),而是可以使用计算值在请求时立即延迟执行解析。然后计算值将为请求缓存解析后的结果,任何进一步的使用都将获得缓存后的值。
具体来说,我们有计算值的计算特性,所需的compute_fresh函数说明如何从头开始计算值,提供的计算方法自动处理缓存:
/// A value that can be computed on-demand from a request.
trait Compute: 'static + Sync + Send + Clone + Sized {
/// Compute the value directly from the given request.
fn compute_fresh(req: &mut Request) -> Self;
/// Compute the value, or return a copy if it has already been computed for this request.
fn compute(req: &mut Request) -> Self { ... }
}
还有一个计算提取器,端点可以用来请求计算数据:
struct Computed<T: Compute>(T);
回到之前的例子,我们可以定义:
struct ParsedQuery { .. }
impl Compute for ParsedQuery { .. }
async fn my_endpoint(query: Computed<ParsedQuery>) { ... }
我们完成了!类似地,中间件可以根据请求直接调用ParsedQuery::compute方法来获取值,可能是从缓存中获取。
我们的希望是,许多传统上可能最终出现在中间件中的功能可以被表示为计算值,这不仅更加符合人体工程学,而且为您的代码推理提供了更多的保证。
What’s next
此时,Tide的存储库和问题跟踪器以及#wg-net-web频道将对不和进行进一步的研究。有很多悬而未决的问题,包括一些设计问题,我鼓励你用你自己的问题和想法来开启更多的问题。随着开发的进展,我们将定期发布更多的博客文章,列出进一步的设计草图和里程碑。
===============
完毕
网友评论