在 Happstack 请求处理程序中捕获程序错误

Catch program errors in Happstack request handler

我在 Haskell 有点菜鸟,所以我不完全确定这是一个 Happstack 问题还是一个一般的 Haskell 问题。

这是我遇到的困难的一个例子。这段代码 "theoretically" 渲染了一些内容,但实际上抛出了一个错误:

throwsError :: String
throwsError = fromJust Nothing

-- no error page
main :: IO ()
main = do
  simpleHTTP nullConf $ do
    decodeBody (defaultBodyPolicy "/tmp/" 4096 4096 4096)
    ok $ toResponse throwsError

这个错误不会导致整个程序崩溃。幸运的是,Happstack 可以捕获处理请求时抛出的任何错误,就像 Web 服务器应该做的那样。然而,遗憾的是它不会向用户显示任何类型的错误页面。它以状态代码 200 和空内容响应。

现在,如果我简单地先输出错误字符串:

-- yes error page
main :: IO ()
main = do
  simpleHTTP nullConf $ do
    decodeBody (defaultBodyPolicy "/tmp/" 4096 4096 4096)
    lift $ putStrLn throwsError -- added this line
    ok $ toResponse throwsError

Happstack returns 状态 500 并显示错误页面。为什么 Happstack 会这样?

ServerPart monad 实现了 MonadThrow,所以我尝试导入 Control.Monad.Catch (handle) 并编写它,但它没有达到我的预期;它再次返回 200 没有内容:

showErrorPage :: SomeException -> ServerPart Response
showErrorPage _ = internalServerError $ toResponse "Error"

-- also no error page
main :: IO ()
main = do
  simpleHTTP nullConf $ handle showErrorPage $ do
    decodeBody (defaultBodyPolicy "/tmp/" 4096 4096 4096)
    ok $ toResponse throwsError

如果不清楚,我想处理所有抛出的错误,这样我就可以记录它们并显示自定义错误页面。 (当然,记录和显示错误页面时抛出的错误除外)。指导将不胜感激。

顺便说一句,我找到了答案。基本上它与惰性评估有关 - Response 对象仅评估为 WHNF 直到 Happstack 在内部开始将数据流式传输到响应主体之后,此时将状态代码从 200 更改为 500 为时已晚.

如果响应是视频流,这是正确的行为,因为在这种情况下,在发送 HTTP 响应之前严格评估响应以确保它不包含任何 undefined 值会很笨拙。然而,对于 实用的已知小响应来说,深入、严格地评估以消除任何可能的底部值显然是不方便的。此外,HTTP 客户端(主要是浏览器)隐含地假设 200 响应代码意味着请求成功 - 并且只有在特定情况下,例如流媒体视频,200 响应以任何方式 "not trusted" 和代码消耗HTTP 响应对其进行更深入的研究,以确保它在处理和显示给最终用户之前格式正确且格式正确 - 确实如我的问题中所见,因为浏览器显示一个空页面并且网络选项卡显示 200 没有任何内容,即使有很多内部服务器错误。

Here 是我针对 Happstack 的拉取请求,它只是将一些类型类添加到 Response 对象,以便可以在将其返回到 [=14 之前对其进行深入、严格的评估=] 单子。那个特定的 PR 有一段麻烦的历史(它曾经做一些范围更大的事情)所以我可能会关闭它并打开一个新的。如果是这样,那一个应该被标记为关闭并且有一个 link 到新的。