2016-03-03 45 views
2

我找不到真正的方式來捕捉純函數在happstack應用程序中拋出的異常。我試過this solution。當IO函數拋出異常時,它可以很好地工作。但是,當純函數拋出異常時,它無法處理它。我的代碼:在happstack中捕捉純函數的例外

{-# 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

爲什麼拋出純代碼的異常?是的,你*可以*,但通常是一個壞主意。如果你避免這樣做,更容易看到究竟發生了什麼。在純代碼中堅持'Either'或'ExceptT',並根據需要僅使用'throwIO'或類似的方法拋出異常。 – dfeuer

回答

2

另一位回答者表示過度懶惰是個問題。在try之前,您可以使用Control.DeepSeq來將表達式評估爲正常形式。

功能更改爲

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和簡單的計算它的參數正常形式返回之前。

它似乎並不像ResponseNFData實例,但是這是相當容易解決,與泛型的幫助:

{-# 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 
+0

我試過了你的完整代碼 - 它工作的很好,但異常處理程序沒有被評估,我得到了標準的happstack錯誤頁面。 –

+1

@AlexanderRazorenov確實代碼中存在錯誤。現在應該修復 - 這次我確​​實嘗試了代碼。它將錯誤顯示爲json - 「{」res「:」「,」err「:」出錯了!「}。 – user2407038

+0

現在,它的工作原理!謝謝! –

3

這是因爲returntoResponse的懶惰。 上線

tryIO mf io = do result <- liftIO $ try io 

somethingWrong是不是在所有的評估,而你的例外是一些更深層次(響應內懶惰字節字符串內),導致它在tryIO逃過try和提高後者未處理。通常在純代碼中的錯誤可能只在被評估爲NF的情況下才會被捕獲,在你的情況下,在main之上。

+0

我該如何解決這個問題? –

+0

@AlexanderRazorenov最好的解決方案是不要在純代碼中拋出異常。或者你可以在評估的地方抓住它。儘管在其他答案中使用了泛型,但我並不認爲在'tryIO'中強制'io'到NF是個好主意。想象一下,可能會發生什麼:讀取一個巨大的文件,等待來自地球另一端的長時間請求等。強制(這應該是)惰性IO操作可能會導致令人驚訝的結果,甚至是令人討厭的後果。 – zakyggaps

+0

你是對的:最好的解決方案是不要在純代碼中拋出異常。但這並不總是可能的。 「真實生活與真實世界不一樣」 –