Zacząłem używać Yesod do opracowania małego projektu, po raz pierwszy używam Haskella do zrobienia czegoś prawdziwego. Ten kod, który obsługuje formularz rejestracyjny działa dobrze:Haskell: obsługa wyjątków w monadach bez IO
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()
Teraz problem jest: czy mogę się zalogować dwukrotnie tych samych poświadczeń dostaję InternalServerError
. To prawda, ponieważ w mojej konfiguracji modelu jest UniqueUser email username
. Chciałbym złapać i poradzić sobie z tym błędem w jakiś sposób. Jak mogę to zrobić i, ogólnie, jak obsługa wyjątków działa w Haskell, kiedy masz do czynienia z monadami nie-IO zdefiniowanymi w zewnętrznej bibliotece lub strukturze?
PS: Przeczytałem samouczek this, ale jest to przydatne, jeśli projektujesz nową bibliotekę. Próbowałem użyć funkcji catch, ale mam wiele błędów typu.
Edit
Dziękuję Ankur, kod pracował z małą modyfikacją, aby usunąć ten błąd:
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)
Kod:
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))
Z ScopedTypeVariables
przedłużenie włączoną
Edycja 2
wersja końcowa, po nutą 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)
Możesz użyć [checkUnique] (http://hackage.haskell.org/packages/archive/persistent/0.3.1.3/doc/html/Database-Persist.html#v:checkUnique), aby sprawdzić, czy klucz jest unikalny przed wstawieniem, i uniknij wyjątku, traktując ten przypadek inaczej. – bennofs
Umh ... nie ma checkUnique w nowszych wersjach Yesod, ale znalazłem [insertUnique] (http://hackage.haskell.org/packages/archive/persistent/latest/doc/html/Database-Persist-Class .html # v: insertUnique), dziękuję. W każdym razie nadal jestem zainteresowany obsługą wyjątków. – andrebask
Możesz użyć rozszerzenia 'ScopedTypeVariables', a następnie wykonaj' (\ (e :: SomeException) -> return False) ' – Ankur