如何将数据库连接传递给我的http处理程序?

时间:2014-09-25 21:00:29

标签: database haskell scope monads

这是我的REST API的简化版本。我正在使用ScottyRethinkDB

现在我必须将数据库连接传递到每个路由处理程序,以便他们可以run查询(请参阅coursesAll)。有超过10条路线,这真的很烦人。我知道我可以在main方法中定义路由处理程序,并且连接句柄h将在范围内,但是它也不会缩放。

我希望能够定义顶级函数,以便将它们放在不同的文件中。如何清理此代码?

我大脑的一部分知道monads可以做到这一点,但是如何? Scotty使用ActionM monad,RethinkDB也有monad,但我不确定如何组合它们。

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE ScopedTypeVariables #-}

import Web.Scotty
import Courses.Course
import qualified Database.RethinkDB as R
import Control.Monad.IO.Class (liftIO)
import Courses.Connection

main :: IO ()
main = do
    h <- connectDb
    scotty 3000 $ do
        get "/" info
        get "/courses" (coursesAll h)

info :: ActionM ()
info = text "Courses v1"

coursesAll :: R.RethinkDBHandle -> ActionM ()
coursesAll h = do
    courses <- liftIO $ R.run h coursesTable
    json $ (courses :: [Course])


connectDb :: IO (R.RethinkDBHandle)
connectDb = do
    connection <- R.connect "localhost" 28015 Nothing
    let connectionDb = R.use connection (R.db "courses")
    return connectionDb

更新: RethinkDB最终没有相关性。真的,我想知道如何将全局配置传递到我的路由中。例如:

{-# LANGUAGE OverloadedStrings #-}

import qualified Web.Scotty
import Web.Scotty.Trans

import Data.Text.Lazy
import Control.Monad.IO.Class (liftIO)

import Control.Monad.Trans.Reader
import Control.Monad.Trans

data Config = Config Text

main :: IO ()
main = do
    let config = Config "Hello World"
    -- how to I make this line work?
    scottyT 3000 id id routes

routes :: ScottyT Text (ReaderT Config IO) ()
routes = do
    get "/" info

info :: ActionT Text (ReaderT Config IO) ()
info = do
    -- this part seems like it works!
    Config message <- lift ask
    text $ "Info: " `append` message

1 个答案:

答案 0 :(得分:1)

您几乎已经知道了更新后的问题,您唯一需要的是向scottyT提供两个跑步者功能。那么让我们来看看签名

scottyT 
  :: (Monad m, MonadIO n)    
  => Port    
  -> (forall a. m a -> n a) 
  -> (m Response -> IO Response)    
  -> ScottyT e m ()  
  -> n ()

m是要嵌入ActionT堆栈的monad,n是运行scottyT时想要的结果monad。

在您的情况下,mReaderT Config IOn只是IO

函数(forall a. m a -> n a)是一个将ReaderT Config IO a计算转换为IO a的函数,我们可以使用runReaderT轻松完成。所以我们来定义

let readerToIO ma = runReaderT ma config

接下来我们需要一个函数将m Response转换为IO Response,但由于在这种情况下nIO相同,我们可以重复使用上面的readerToIO功能。因此

main = do
    let config = Config "Hello World"
        readerToIO ma = runReaderT ma config
    scottyT 3000 readerToIO readerToIO routes