Haskell Servant中serveWithContext的作用是什么?它能实现什么ReaderT做不到的功能?
Context与ReaderT的核心差异
两者根本不是同一层面的组件,作用域完全不重叠:
- ReaderT是业务Handler层的依赖注入方案,仅对你自己编写的业务逻辑可见、可用。
- Context是Servant框架层的依赖注入方案,专门给Servant内置的路由匹配、前置校验逻辑提供配置,这些逻辑运行在你自定义的Handler执行之前,完全感知不到你自己定义的ReaderT Monad栈,这就是ReaderT覆盖不到的场景。
比如你用的servant-auth组件的JWT校验、Cookie合法性校验,都是Servant在匹配到对应路由后,首先运行的框架内置逻辑,这时候你的ReaderT栈还没初始化,自然没法把配置通过ReaderT传给这些逻辑,必须通过Context传递。
你看到的"两次传参"不是重复,是用途完全不同
你给出的示例代码里的两次传参,分别对应两个不同的使用方:
-- 这部分是给框架层逻辑用的配置 cfg = defaultCookieSettings :. jwtCfg :. EmptyContext
上面塞进Context的配置,是给servant-auth的内置校验逻辑用的:比如校验请求带的JWT签名是否合法、Cookie是否在有效期内,都是用的Context里的这组配置。
-- 这部分是给你自己写的业务Handler用的配置 serveWithContext api cfg (server defaultCookieSettings jwtCfg)
这里手动传入server函数的配置,是给你自己编写的业务逻辑用的:比如你在checkCreds里需要手动生成JWT返回给客户端、手动给响应设置自定义Cookie,这时候用的就是你手动传的这组配置。
如果你的业务逻辑里不需要操作JWT、Cookie配置,完全可以不用手动传这部分参数,只保留Context里的配置即可,不存在强制重复传参的要求。
Context的设计逻辑
Context本质是类型安全的异构列表,Servant的内置逻辑可以根据需要的配置类型,自动从Context列表中查找对应的值,不需要使用者手动给框架逻辑传参,既保证了类型安全,又把框架层和业务层的配置注入完全隔离开,互不干扰。
内容的提问来源于stack exchange,提问作者Joshua Billson
相关产品推荐
相关产品推荐

