如何在 Yesod 中捕获异常而不杀死站点?
Posted
技术标签:
【中文标题】如何在 Yesod 中捕获异常而不杀死站点?【英文标题】:How can I catch exceptions within Yesod without killing the site? 【发布时间】:2012-07-22 01:21:24 【问题描述】:我是 Yesod 和 Haskell 的新手,所以请多多包涵,但我有一个程序可以接受文件,计算单词并在新的 Yesod 网页上显示结果。我需要一种更优雅地捕获异常的方法。例如,如果它在隐藏文件上运行以下函数:
wordCount :: String -> String -> String
wordCount ('.' : _) _ = error "Cannot count hidden files."
wordCount name contents = "<p>There are <b>" ++ show (length $ words contents) ++ "</b> words in your file <i>" ++ name ++ "</i>.</p>"
我想显示该错误消息,然后显示表单以供用户提交新文件。现在它只是转到一个页面,上面写着“页面加载时与服务器的连接已重置。”
我认为 Yesod 有自己的一组 catch 函数,我可能不得不隐藏它来编写自己的函数,但我不确定它们会在哪里,而且我在任何文档中都找不到对它们的任何引用。如果用户输入了错误的文件,我不希望网站崩溃。
任何帮助将不胜感激。
编辑:感谢您的建议。我意识到以这种方式抛出错误并不是处理错误的最佳方法,但问题实际上是我正在使用通常从终端而不是 Yesod 站点运行的现有 Haskell 代码。我用 wordcount 作为一个小例子,但实际上它是几十个文件,几十个函数和分散的错误抛出。希望我可以捕获这些,而不必完全修改现有代码中的每个函数。如果有人对如何在 Yesod 中捕获异常有任何建议,我将不胜感激。
【问题讨论】:
不要将error
用于此类异常。使用Either ErrorMessage ReturnType
或其他一些错误机制。
关于错误机制,最近有一篇关于 Hackage 上新的 errors 包的不错的博客文章说明了使用 Either
、Maybe
等的一些基本错误处理技术。
【参考方案1】:
正如 dflemstr 所说,不要将error
用于此类可恢复的错误。错误机制类似于undefined
——它代表一个完全灾难性的错误,例如尚未编写的函数或无限循环。对于您希望永远遇到坏情况的部分函数来说,这很好,但对于预期和必须处理的错误来说,它并不是最佳选择。此外,类型系统不反映您对error
的使用,因此您的函数的使用者无法知道捕获错误。
相反,您应该使用像 Maybe
或 Either
这样的类型来表示这样的错误。例如,你可以这样重写这个函数:
wordCount :: String -> String -> Maybe String
wordCount ('.' : _) _ = Nothing
wordCount name contents = Just $ "<p>There are <b>" ++ show (length $ words contents) ++ "</b> words in your file <i>" ++ name ++ "</i>.</p>"
现在,不是返回 String
,而是返回 Maybe String
。这是一个可以通过Nothing
或Just String
的类型。 Nothing
代表失败——即函数由于输入无效而失败。 Just
代表成功。
接下来,要“捕捉”错误,您可以只进行模式匹配:
case wordCount fileName contents of
Just res -> res
Nothing -> "Cannot count hidden files!"
Data.Maybe 中还有一些方便的功能,可以让您的代码在常见情况下更整洁。
另一个选项是Either
类型。在Maybe
类型中,Nothing
构造函数不携带任何附加信息。您所知道的是该功能失败。 Either
的行为方式相同,除了“失败”情况确实携带任意附加信息。 “失败”案例称为Left
,“成功”案例称为Right
。该类型还有两个参数:错误信息的类型和结果的类型。它看起来像这样:
wordCount :: String -> String -> Either String String
wordCount ('.' : _) _ = Left "You cannot count a hidden file!"
wordCount name contents = Right $ "<p>There are <b>" ++ show (length $ words contents) ++ "</b> words in your file <i>" ++ name ++ "</i>.</p>"
然后您可以像Maybe
一样处理结果。在这种情况下,我认为Maybe
更可取,因为该函数只有一种方法会失败。如果有很多可能的错误情况,Either
会是更好的选择。
【讨论】:
以上是关于如何在 Yesod 中捕获异常而不杀死站点?的主要内容,如果未能解决你的问题,请参考以下文章
使用 KeyboardInterrupt 异常捕获 SIGINT 在终端中有效,而不是在脚本中