我无法找到真正的方法来捕获由happstack应用程序中的纯函数引发的异常。我试过了this solution。当IO函数抛出异常时,它运行良好。但是当纯函数抛出异常时,它无法处理它。我的代码:
{-# LANGUAGE DeriveDataTypeable #-}
module Main where
import Prelude hiding(catch)
import Control.Monad (msum, mzero, join)
import Control.Monad.IO.Class(liftIO)
import Happstack.Server
import Text.JSON.Generic
import qualified Data.ByteString.Char8 as B
import Control.Exception
data Res = Res {res :: String, err :: String} deriving (Data, Typeable)
evaluateIt :: Res
evaluateIt = throw (ErrorCall "Something goes wrong!")
somethingWrong :: IO Response
somethingWrong = return $ toResponse $ encodeJSON $ evaluateIt
errorHandler :: SomeException -> ServerPart Response
errorHandler e = ok $ setHeaderBS (B.pack "Content-Type") (B.pack "application/json") $ toResponse $ encodeJSON $ Res {err = show e, res = ""}
indexHTML = tryIO (Just errorHandler) somethingWrong
main :: IO ()
main = do
simpleHTTP nullConf $ msum [ indexHTML ]
tryIO :: Maybe (SomeException -> ServerPart Response)
-> IO a
-> ServerPart a
tryIO mf io = do result <- liftIO $ try io
case (result) of Right good -> return good
Left exception -> handle exception mf
where handle exception (Just handler) = escape $ handler exception
handle _ Nothing = mzero
我哪里错了?
答案 0 :(得分:3)
这是因为return
和toResponse
的懒惰。
在线
tryIO mf io = do result <- liftIO $ try io
somethingWrong
根本没有被评估,而你的异常更深一些(在Response中的一个惰性字节串内),导致它逃脱try
中的tryIO
并被提升未处理。通常,只有在main
之上的情况下,纯代码中的错误才会被捕获到被评估为NF的位置。
答案 1 :(得分:2)
另一位回答者表示过度懒惰是个问题。您可以使用Control.DeepSeq
在try
之前将表达式计算为普通形式来修复它。
将功能更改为
import Control.DeepSeq
...
tryIO :: NFData a => Maybe (SomeException -> ServerPart Response) -> IO a -> ServerPart a
tryIO mf io = do
result <- liftIO $ io >>= try . return . force
...
force
具有类型NFData a => a -> a
,并在返回之前简单地将其参数计算为普通形式。
Response
似乎没有NFData
个实例,但在Generics的帮助下,这很容易修复:
{-# LANGUAGE StandaloneDeriving, DeriveGeneric #-}
...
import Control.DeepSeq
import GHC.Generics
...
deriving instance Generic Response
deriving instance Generic RsFlags
deriving instance Generic HeaderPair
deriving instance Generic Length
instance NFData Response
instance NFData RsFlags
instance NFData HeaderPair
instance NFData Length
复制粘贴的完整代码:
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE StandaloneDeriving, DeriveGeneric #-}
module Main where
import Prelude hiding(catch)
import Control.Monad (msum, mzero, join)
import Control.Monad.IO.Class(liftIO)
import Happstack.Server
import Text.JSON.Generic
import qualified Data.ByteString.Char8 as B
import Control.DeepSeq
import GHC.Generics
import Control.Exception
data Res = Res {res :: String, err :: String} deriving (Data, Typeable)
evaluateIt :: Res
evaluateIt = throw (ErrorCall "Something goes wrong!")
somethingWrong :: IO Response
somethingWrong = return $ toResponse $ encodeJSON $ evaluateIt
errorHandler :: SomeException -> ServerPart Response
errorHandler e = ok $ setHeaderBS (B.pack "Content-Type") (B.pack "application/json") $ toResponse $ encodeJSON $ Res {err = show e, res = ""}
indexHTML = tryIO (Just errorHandler) somethingWrong
main :: IO ()
main = do
simpleHTTP nullConf $ msum [ indexHTML ]
deriving instance Generic Response
deriving instance Generic RsFlags
deriving instance Generic HeaderPair
deriving instance Generic Length
instance NFData Response
instance NFData RsFlags
instance NFData HeaderPair
instance NFData Length
tryIO :: NFData a => Maybe (SomeException -> ServerPart Response) -> IO a -> ServerPart a
tryIO mf io = do
result <- liftIO $ try $ io >>= \x -> x `deepseq` return x
case (result) of
Right good -> return good
Left exception -> handle exception mf
where handle exception (Just handler) = escape $ handler exception
handle _ Nothing = mzero