将Servant.Generic路由与ReaderT(池连接)IO结合使用

时间:2018-07-22 13:40:26

标签: haskell servant

我正在使用servant-generic-0.1.0.3servant-server-0.13.0.1执行以下操作:

data Site route = Site
  { page :: route :-
      "page" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] [Int]
  , home :: route :-
      Raw
  } deriving (Generic)

type API = ToServant (Site AsApi)

siteServer :: Pool Connection -> Site AsServer
siteServer pool = Site
  { page = \x y ->
      liftIO $ withResource pool $ \conn -> someDbFunction conn x y
  , home = serveDirectoryWebApp "static"
  }

api :: Proxy API
api = Proxy

app :: Pool Connection -> Application
app pool = serve api (toServant $ siteServer pool)

那很好,然后我尝试使用ReaderT避免将Pool Connection传递到siteServer,所以我添加了AppM并替换了siteServer,如下所示:

type AppM = ReaderT (Pool Connection) IO

siteServer :: ServerT API AppM
siteServer = Site
  { page = do
      pool <- ask
      \x y ->
        liftIO $ withResource pool $ \conn -> someDbFunction conn x y
  , home = serveDirectoryWebApp "static"
  }

但是当我尝试编译它时出现了很多错误。

我遵循了servant cookbook中所示的相同步骤,但是尽管使用常规路由时可以使用通用路由,但我无法使用通用路由。

我错过了可以使之工作的东西吗?

1 个答案:

答案 0 :(得分:1)

至少对于servant- *> = 0.14(请参阅here)支持的记录式路由,如果要使用除Handler以外的其他monad,则需要查看{ {1}}和AsServerT

在您的示例中,这意味着genericServerT的定义应如下(未经类型检查,但应非常接近正确)。

siteServer

编辑:由于您使用的是servant- * toServant替换为siteServer :: Site (AsServerT AppM) siteServer = Site { page = ... something in AppM ... , home = ... something in AppM ... } -- turning that into a usual chain of :<|>-separated handlers oldStyleServer :: ServerT API AppM oldStyleServer = genericServerT siteServer -- bringing it all back in Handler oldStyleServerInHandler :: Pool Connection -> Server API -- same as ServerT API Handler oldStyleServerInHandler conns = hoistServer (Proxy @API) appToHandler oldStyleServer where appToHandler = liftIO . flip runReaderT conns -- or something along those lines -- serving it app :: Pool Connection -> Application app conns = serve (Proxy @API) (oldStyleServerInHandler conns)