问题描述
在这种情况下,为什么异常匹配处理程序excToStr
无法捕获模式匹配失败?
我在Scotty Web framework的控制下有一个传入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
来自safe-exceptions库。我还尝试了其他功能(catchAny
,handle
,catch
等),但均未成功。问题的症结在于,如果无法成功解码传入的正文(并且decode
返回nothing
而不是Just x
),则模式匹配将失败,因此我希望我的{{1 }}(即extToStr
)将处理它,因为excH
(和catchAnyDeep
)将处理 ANY 异常(包括模式匹配失败,对吧?): / p>
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
抛出一个异常,则它按预期方式工作(该异常被捕获)。但是模式匹配失败会导致HTTP内部错误500,并显示一条消息“ ...中do表达式中的模式匹配失败”。如何处理模式匹配异常?
解决方法
在侦查动作中(类型ActionT Text IO
)有两种形式的异常。 IO
中存在本机异常,而ActionT
转换器添加了另一种形式。这些异常单独处理。该接口由the instances of ActionT
给出:
-
(MonadCatch m,ScottyError e) => MonadCatch (ActionT e m)
(和类似的MonadThrow
实例)。这表明当您使用catch
中的MonadCatch
(或throwString
中的MonadThrow
,以及 safe-exceptions 库中的其他变体)时,正在使用转换后的单子m
的错误处理机制,在Scotty中通常是IO
(它定义了ActionM = ActionT Text IO
)。 -
(Monad m,ScottyError e) => MonadFail (ActionT e m)
。MonadFail
是do
块中用于部分模式匹配的约束。它不需要基础单子MonadFail
的{{1}},这表明与m
/MonadThrow
不同,它使用了MonadCatch
提供的异常机制变压器本身。要捕获此异常,您必须在Scotty中寻找组合器,而不是诸如rescue
之类的辅助库。