0

我希望我的默认处理程序能够捕获我的应用程序抛出的所有异常,但为了实现这一点,我需要在手动添加一些异常捕获后手动调用 raise 我的 IO 代码。

下面是一个示例最小服务器:

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DeriveFunctor #-}
{-# LANGUAGE ScopedTypeVariables #-}

module Lib
    ( someFunc
    ) where

import Web.Scotty.Trans
import Control.Monad.Trans
import Control.Monad.Reader
import Control.Monad.Catch
import Control.Monad.Except
import Data.Text.Lazy as TL

data AppEnv = AppEnv
  { appStuff :: String
  } 

newtype App a = App 
  { unApp :: ReaderT AppEnv IO a 
  } deriving (Functor, Applicative, Monad, MonadIO, MonadReader AppEnv, MonadThrow)

someFunc :: IO ()
someFunc = do
  let run a  = runReaderT (unApp $ App a) (AppEnv "APPY STUFF")
  scottyT 8080 run $ do

    defaultHandler $ \(e :: TL.Text) -> do
      liftIO $ print "HERE"
      liftIO $ print $ showError e
      html $ "Something Went Seriously Wrong"

    get "/" $ do
      (r :: (Either TL.Text String)) <- liftIO $ runExceptT $ do
        (uId) <- lift $ readFile "./helloworld.txt"
        return $ ("hello") 
      liftIO $ print r
      case r of
        Left l -> raise l
        Right s -> (html "hello world")

    get "/catch-this" $ do
      error "Catch Me"
      (html "hello world")

    notFound $ do
     html "That is not a valid route"

我希望能够在我的默认处理程序中捕获所有未捕获的异常,但这不是 scotty 的默认行为,只有在你调用raise. 我可以将我所有的 ActionM 代码块都包装在其中,ExceptT但这似乎是解决此问题的一种混乱/机械的方式。我主要是为了记录目的而这样做,这样我就可以向 Sentry 报告或记录到文件中,这样会更方便。

4

1 回答 1

0

我想我会把它扔在那里,因为我最近又在寻找同样的解决方案。很不幸,但我永远无法得到我想要的行为scotty

幸运的是,因为scotty它只是一个很好的库来创建WAI应用程序,所以你可以很好地使用Settings类型 fromwarpOptions类型 from scotty

以下是如何处理此问题的示例:

{-# LANGUAGE OverloadedStrings #-}

module Lib
    ( someFunc
    ) where

import Web.Scotty.Trans
import Data.Text
import qualified Data.Text.Lazy as TL
import Control.Monad.IO.Class
import Control.Exception
import Network.HTTP.Types
import System.IO.Error
import Network.Wai.Handler.Warp
import Network.Wai

myOpts :: Options 
myOpts = Options 1 mySettings 

mySettings :: Settings 
mySettings = setOnExceptionResponse myHandler $ setPort 3002 $  defaultSettings 

myHandler :: SomeException -> Response
myHandler se = responseLBS status500 [] "HERE WE ARE"

someFunc :: IO ()
someFunc = do
  scottyOptsT myOpts id routes

myExceptions :: (MonadIO m) => TL.Text -> ActionT TL.Text m ()
myExceptions t = do
  liftIO $ print t
  html "error"

routes :: (MonadIO m) => ScottyT TL.Text m ()
routes = do
  defaultHandler $ \str -> do
   liftAndCatchIO $ print str
   status status500
   json ("welp you thought"::Text)
  get "/:here" $ do
    liftIO $ ioError $ userError "Hahah"
    text "here"

然后,您可以进一步挖掘Settings提供的类型,warp以便您可以记录所有错误消息或使用以下方法执行一些自定义操作setOnException setOnExceptionResponse

于 2020-10-16T00:17:23.133 回答