and*_*ask 6 monads haskell exception-handling yesod
我开始使用Yesod开发一个小项目,这是我第一次使用Haskell做一些真实的事情.这个处理注册表单的代码工作正常:
postRegisterR :: Handler ()
postRegisterR = do email <- runInputPost $ ireq textField "email"
user <- runInputPost $ ireq textField "user"
pwd <- runInputPost $ ireq textField "pwd"
cpwd <- runInputPost $ ireq textField "cpwd"
if pwd == cpwd && isValidEmail email
then do
tryInsert email user pwd
setSession "user" user
redirectUltDest SessionR
else do
redirect HomeR
tryInsert :: Text -> Text -> Text -> Handler ()
tryInsert email user pwd = do pwdbs <- liftIO $ hashedPwd pwd
_ <- runDB $ insert $ User email user pwdbs
return ()
Run Code Online (Sandbox Code Playgroud)
现在的问题是:如果我使用相同的凭据登录两次,我会得到一个InternalServerError
.这是对的,因为在我的模型配置中有UniqueUser email username
.所以我想以某种方式捕捉和处理这个错误.我怎么能这样做,一般来说,当你处理外部库或框架中定义的非IO monad时,异常处理在Haskell中是如何工作的?
PS:我读过这个教程,但如果你正在设计一个新的库,这很有用.我尝试使用catch函数,但是我遇到了很多类型错误.
编辑
谢谢Ankur,您的代码稍作修改,以消除此错误:
Ambiguous type variable `e0' in the constraint:
(Exception e0) arising from a use of `catch'
Probable fix: add a type signature that fixes these type variable(s)
Run Code Online (Sandbox Code Playgroud)
码:
tryInsert :: Text -> Text -> ByteString -> Handler Bool
tryInsert email user pwd = HandlerT (\d -> catch (unHandlerT (runDB $ insert $ User email user pwd) d
>> return True)
(\(e :: SomeException) -> return False))
Run Code Online (Sandbox Code Playgroud)
随着ScopedTypeVariables
扩展启用
编辑2
最终版本,在bennofs的暗示之后:
{-# LANGUAGE ScopedTypeVariables #-}
import Control.Exception.Lifted (catch)
import Control.Monad (void)
postRegisterR :: Handler ()
postRegisterR = do email <- runInputPost $ ireq textField "email"
user <- runInputPost $ ireq textField "user"
pwd <- runInputPost $ ireq textField "pwd"
cpwd <- runInputPost $ ireq textField "cpwd"
if pwd == cpwd && isValidEmail email
then do
pwdbs <- liftIO $ hashedPwd pwd
success <- tryInsert email user pwdbs
case success of
True -> do setSession "user" user
redirectUltDest SessionR
False -> redirect HomeR
else do
redirect HomeR
tryInsert :: Text -> Text -> ByteString -> Handler Bool
tryInsert email user pwd = do void $ runDB $ insert $ User email user pwd
return True
`catch` (\(e :: SomeException) ->
do return False)
Run Code Online (Sandbox Code Playgroud)
有一个名为lifted-base的包,它还提供了更通用的catch函数:
Control.Exception.Lifted.catch ::
(MonadBaseControl IO m, Exception e)
=> m a -- ^ The computation to run
-> (e -> m a) -- ^ Handler to invoke if an exception is raised
-> m a
Run Code Online (Sandbox Code Playgroud)
有一个实例MonadBaseControl IO Handler,所以你可以使用这个函数:
{-# LANGUAGE ScopedTypeVariables #-} -- I think this is needed PatternSignatures.
import Control.Exception.Lifted (catch)
import Control.Monad (void)
tryInsert :: Text -> Text -> Text -> Handler ()
tryInsert email user pwd = do
pwdbs <- liftIO $ hashedPwd pwd
(void $ runDB $ insert $ User email user pwdbs) `catch` \(e :: SomeException) -> do
-- Your exception handling goes code here. This code also lives in the Handler monad.
return ()
return ()
Run Code Online (Sandbox Code Playgroud)
另一种可能性是使用MonadCatchIO-mtl,它也提供了通用的catch函数.MonadCatchIO-mtl不会建立在GHC HEAD上.我还认为使用insertUnique
是最干净的方法来处理这个问题.
你可以尝试如下所示的方法,基本上Handler
是HandlerT
monad 转换器(我还没有输入检查下面的代码:))
tryInsert :: Text -> Text -> Text -> Handler Bool
tryInsert email user pwd = HandlerT (\d -> do pwdbs <- hashedPwd pwd
catch (unHandlerT (runDB $ insert $ User email user pwdbs) d >> return True)
(\e -> return False))
Run Code Online (Sandbox Code Playgroud)
并检查返回的bool值是否有异常。
归档时间: |
|
查看次数: |
779 次 |
最近记录: |