请求上下文

actix-webaxumntex等框架不同,我们并不推荐使用提取器(Extractor)模式1, 而是采取类似于ExpressFiber那种把很多方法都挂在请求上下文的做法,这会带来几个好处:

  1. 保持controller中只有Request一个参数,便于批量实现接口以及代码自动生成;
  2. 允许使用者动态判断是否需要从请求中获取信息,避免使用不必要的Option类型;
  3. 能够提供非异步的实现,比如从URI中提取查询参数这种本来就应该是同步的2

当然,由于我们提供了对actix-webaxumntex等框架的集成,它们所支持的Handler都是可以直接在Zino中使用的, 也包括提取器模式。

我们通过RequestContext这一trait来提供请求上下文方法,它包含以下必须要实现的方法:

pub trait RequestContext {
    type Method: AsRef<str>;
    type Headers;

    fn request_method(&self) -> &Self::Method;

    fn original_uri(&self) -> &Uri;
    fn matched_route(&self) -> Cow<'_, str>;

    fn header_map(&self) -> &Self::Headers;
    fn get_header(&self, name: &str) -> Option<&str>;
    fn client_ip(&self) -> Option<IpAddr>;

    fn get_context(&self) -> Option<Context>;

    fn get_data<T: Clone + Send + Sync + 'static>(&self) -> Option<T>;
    fn set_data<T: Clone + Send + Sync + 'static>(
        &mut self,
        value: T
    ) -> Option<T>;

    async fn read_body_bytes(&mut self) -> Result<Vec<u8>, Error>;
}

值得注意的是,我们提供的read_body_bytes方法不会消费Request对象3,但是它的重复调用并不保证会给出相同的结果, 具体处理方式由实现决定。

1

有些人总觉得Rust中的提取器很神奇,其实无外乎就是泛型加上宏批量实现罢了,Rust本身并不支持可变参数函数。

2

axumQuery提取器尽管提供了同步的try_from_uri方法,但它实现的from_request_parts却是异步的。

3

axum中,实现了FromRequest的提取器总会消费Request对象,所以它们只能使用一次,并且只能作为Handler的最后一个参数。