2

excToStr为什么在这种情况下我的异常处理程序没有捕捉到模式匹配失败?

我有一个在Scotty Web 框架控制下的传入 POST 请求的处理程序:

...
import qualified Web.Scotty as W
...

    W.post "/some/endpoint" $ excToStr "Cannot handle it!" $ do
        b <- W.body

        -- throwString "aaa" <--- THIS IS HANDLED FINE!

        Just x::Maybe SomeMyType <- pure (decode b) -- BUT NOT THIS PATTERN-MATCHING FAILURE!
        liftIO $ print $ show x
        W.text "OK"

我的在哪里excToStr,它看起来像:

...
import qualified Data.Text.Lazy as LT
...

excH :: (String -> String) -> ActionT LT.Text IO () -> ActionT LT.Text IO ()
excH mkErr m = catchAnyDeep m (W.text . cs . mkErr . show)

excToStr :: String -> ActionT LT.Text IO () -> ActionT LT.Text IO ()
excToStr errMsg = excH (\details -> errMsg <> " (" <> details <> ")")

catchAnyDeep来自安全异常库。我也尝试了其他功能(catchAny, handle,catch等) - 没有成功。问题的症结在于,当传入的正文无法成功解码(并decode返回Nothing而不是返回Just x)时,模式匹配失败,所以我希望我的extToStr(即excH)会处理它,因为catchAnyDeep(和catchAny)处理任何异常(包括模式-匹配失败,对吧?):

catchAny :: MonadCatch m => m a -> (SomeException -> m a) -> m a

catchAnyDeep :: (MonadCatch m, MonadIO m, NFData a) => m a -> (SomeException -> m a) -> m a.

如果我只抛出一个异常,throwString那么它会按预期工作(异常被捕获)。但是模式匹配失败会导致 HTTP 内部错误 500,并显示消息“在 do 表达式中的模式匹配失败 ....”。如何处理模式匹配异常?

4

1 回答 1

3

ActionT Text IOscotty 动作(类型)有两种形式的异常。中存在本机异常,以及由转换器IO添加的另一种形式。ActionT这些异常单独处理。接口由以下实例给出ActionT

  • (MonadCatch m, ScottyError e) => MonadCatch (ActionT e m)(和类似的MonadThrow实例)。这表明当您使用catchfrom MonadCatch(或throwStringfrom以及安全异常MonadThrow库中的其他变体)时,您正在使用转换后的 monad 的错误处理机制,这在 Scotty 中通常是(它定义了)。mIOActionM = ActionT Text IO

  • (Monad m, ScottyError e) => MonadFail (ActionT e m). MonadFail是用于do块中的部分模式匹配的约束。它不需要MonadFail来自底层 monad的 a m,这表明与MonadThrow/不同MonadCatch,它使用ActionT转换器本身提供的异常机制。要捕获此异常,您必须在 Scotty 中寻找组合器,而不是辅助库,例如rescue.

于 2020-11-05T20:08:39.973 回答