我希望我的问题是一个通用的问题,实际上我正在使用 wreq 更具体地使用 Web API 查询。 Web 服务请求一些不记名 <> token 授权,我设法发布了一个请求r = post (serveraddress ++ "/api/Integration/Login") (toJSON (AuthDataStruct "user" "xxxxxx"))
我得到了回复 r
我可以从中提取“token_type”和“access_token”
accessToken = r >>= (\ x -> return $ x ^. responseBody . key "access_token" . _String)
tokenType = r >>= (\ x -> return $ x ^. responseBody . key "token_type" . _String)
但这些数据是 IO 类型(IO 文本 - 准确地说),问题是需要这些数据来构建授权 header authHeader :: IO Network.Wreq.Options
authHeader = do
tk <- tokenType
at <- accessToken
let auth = AccessToken (unpack tk) (unpack at)
return (opts auth)
opts token = defaults & header "Authorization" .~ [BI.packChars (tokenType token ++ " " ++ accessToken token)]
然后用于对 Web 服务发出的每个请求:webQueryGetProperties :: IO (Response Data.ByteString.Lazy.Internal.ByteString)
webQueryGetProperties = do`
opt <- authHeader
let sql = GetDocument 1 ""
postWith opt (serveraddress ++ "/api/Integration/GetProperties") (toJSON sql)
webQueryGetDataValues etc...
不要太关注代码尝试按照我的推理,我想设置一个配置数据结构来存储 token 信息以避免运行所有IO只是为了得到我已经拥有的.. .(这是资源的浪费,不是DRYI) token 只需要在一段时间后更新。理想的情况是有一个配置构建器,它使用从 IO 操作中提取的数据创建存储的 token 数据类型。如果我没有错的话,在 Haskell 中是不可能的(无法逃脱 monad),可能的解决方案是什么:
在命令式语言中,您只需从结果中获取 token 数据并将其提供给构造函数,您就可以一劳永逸地设置“全局”配置。
最佳答案
低技术模式是简单地接受您的配置作为参数。
webQueryGetProperties :: Options -> IO (Response ByteString)
webQueryGetProperties o = postWith o (...) (...)
是的,所有依赖于配置的东西,甚至是传递性的,都必须把它作为一个参数。然后,在顶层,您构建一次配置。
main = do
o <- authHeader
webQueryGetProperties o
webQueryGetDataValues o
-- etc.
有时候用起来很方便ReaderT
为你传递争论。值得注意的是,这使得编写不需要参数的操作成为可能,而这种方式却忽略了它们实际上是在为您传递参数这一事实。webQueryGetProperties :: ReaderT Options IO (Response ByteString)
webQueryGetProperties = do
o <- ask
liftIO (postWith o (...) (...))
doesn'tNeedOptions :: MonadIO m => IO String
doesn'tNeedOptions = liftIO $ do
putStrLn "wow"
getLine
main = do
o <- authHeader
flip runReaderT o $ do
webQueryGetProperties
doesn'tNeedOptions -- configuration gets passed to this but ignored automatically
webQueryGetDataValues
实际上,在这种风格中,人们通常会编写更多的类多态性,所以webQueryGetProperties :: (MonadReader Options m, MonadIO m) => m (Response ByteString)
-- implementation doesn't change
如果您需要能够注意到 token 已过期并需要替换为不同的 token ,您可以升级到 StateT
.webQueryGetProperties :: (MonadState Options m, MonadIO m) => m (Response ByteString)
webQueryGetProperties = do
o <- get
liftIO (postWith o (...) (...))
checkAndRenew :: (MonadState Options m, MonadIO m) => m ()
checkAndRenew = do
renewp <- gets needsToBeRenewed
when renewp (authHeader >>= put)
关于haskell - 从 IO Monad 设置配置参数 - 设计模式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65496635/