1

我正在使用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 ConnectionsiteServer,所以我添加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中显示的相同步骤,但我无法使用通用路由进行此操作,尽管它在使用常规路由时有效。

我是否错过了可以使这项工作发挥作用的东西?

4

1 回答 1

1

至少对于servant-* >= 0.14(见这里)支持的记录风格的路线,如果你想使用另一个monad而不是Handler,你会想看看AsServerTand genericServerT

应用于您的示例,此方法siteServer应定义如下(未经过类型检查,但应非常接近正确)。

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)

编辑:由于您使用servant-* < 0.14和servant-generic,您应该替换genericServerTtoServant.

于 2018-07-22T16:09:09.870 回答