我是Haskell初学者,目前正在使用wreq在API周围制作一个简单的包装器。 如果提供时间,我想发送一个if-modified-since
标头。 我这样做的方式如下。
getResponse :: (FormatTime t, Exception e) => File -> Maybe t -> IO (Either e (Response L.ByteString))
getResponse file (Just t) =
let formattedTime = (B.pack . formatTime defaultTimeLocale rfc822DateFormat) t
opts = defaults & header "if-modified-since" .~ [formattedTime]
in try $ getWith opts $ buildUrl file
getResponse file Nothing = try $ (get $ buildUrl file)
我注意到304 (not modified)
响应作为异常返回,因此这是我使用Either
类型的理由。 我想为可能使用此 api 包装器的人提供错误的可见性。
假设请求成功,我想将响应正文解析为库中定义的相应类型。如果我正在向其发出请求的服务器上发生更改,则反序列化可能无法正常工作,因此我选择使用Maybe
类型来说明这一点。
getPayload :: FromJSON b => (Either e (Response L.ByteString)) -> Either e (Maybe b)
getPayload (Left _) = return Nothing
getPayload (Right a) = return $ fmap Just (^. responseBody) =<< asJSON a
这些功能的签名对我来说开始变得碍眼,我的直觉告诉我有更好的方法,但我不确定。我做的一件事是制作另一个功能将它们放在一起,希望它更容易使用。 这是我计划用来创建其他函数以对单个资源发出更具体请求的函数。
getResource :: (Exception e, FormatTime t, FromJSON b) => File -> Maybe t -> IO (Either e (Maybe b))
getResource f t = getPayload <$> (getResponse f t)
我现在在处理 http 请求时必须处理 3 层结构。IO
、Either
和Maybe
。 我把这弄得太复杂了吗?从使用和可维护性的角度来看,我该怎么做才能减少使用时的痛苦?我怎样才能改善这一点?
这可能不是你想要的,但asJSON
的返回类型m (Response a)
,其中m
是一个MonadThrow
。虽然Maybe
是一个MonadThrow
实例,但Either e
也是如此。这意味着您不必使用Maybe
来处理asJSON
是否出现任何问题。你可以"停留"在Either
的monad中:
getPayload :: FromJSON b => Either SomeException (Response L.ByteString)
-> Either SomeException b
getPayload = ((fmap (^. responseBody) . asJSON) =<<)
显然,这对左侧的错误类型施加了额外的约束,所以我不确定这是否可以接受。如果没有,请发表评论。