如何使用CherryPy捕获所有异常?

6

我使用 CherryPy 运行一个非常简单的Web服务器。它旨在处理GET参数,并且如果它们正确,会对它们进行某些操作。

import cherrypy

class MainServer(object):
    def index(self, **params):
        # do things with correct parameters
        if 'a' in params:
            print params['a']

    index.exposed = True

cherrypy.quickstart(MainServer())

例如,
http://127.0.0.1:8080/abcde:

 404 Not Found

The path '/abcde' was not found.

Traceback (most recent call last):
  File "C:\Python27\lib\site-packages\cherrypy\_cprequest.py", line 656, in respond
    response.body = self.handler()
  File "C:\Python27\lib\site-packages\cherrypy\lib\encoding.py", line 188, in __call__
    self.body = self.oldhandler(*args, **kwargs)
  File "C:\Python27\lib\site-packages\cherrypy\_cperror.py", line 386, in __call__
    raise self
NotFound: (404, "The path '/abcde' was not found.")
Powered by CherryPy 3.2.4

我试图捕获这个异常并展示一个空白页面,因为客户并不关心它。具体而言,无论导致异常的url或查询字符串如何,结果都将是一个空的主体。

我查看了有关错误处理的文档 cherrypy._cperror,但我没有找到实际使用它的方法。

注意:我放弃使用CherryPy并找到了一个简单的解决方案,使用BaseHTTPServer见下面我的答案


为什么不使用 try, except - Dmitry Zagorulkin
@ZagorulkinDmitry:我尝试了,但显然这不起作用(请参见我上面的更新) - WoJ
如果客户不在意,这是什么原因?你还想要404状态但没有内容吗?你担心的是回溯文本吗? - jwalker
@jwalker:客户端发送了一个带有信息的查询,但这是单向消息。他们不期望收到任何回复(甚至不需要代码 - 我可以始终返回200)。然后我在我的脚本中使用他们发送的内容。是的 - 我担心响应中的回溯文本(我想要一个空的响应)。 - WoJ
8个回答

5

文档中似乎缺少这一部分。当我查找源代码中自定义错误处理的详细说明时,我找到了以下内容。

自定义错误处理

预期HTTP响应

'error_page'配置命名空间可用于提供自定义HTML输出以响应预期的响应(例如404未找到)。提供将读取输出的文件名。使用纯老式Python字符串格式化操作插入值%(status)s、%(message)s、%(traceback)s和%(version)s。

_cp_config = {
    'error_page.404': os.path.join(localDir, "static/index.html")
}

从3.1版本开始,您还可以提供一个函数或其他可调用对象作为error_page条目。它将接收与模板插值相同的状态、消息、回溯和版本参数。

def error_page_402(status, message, traceback, version):
    return "Error %s - Well, I'm very sorry but you haven't paid!" % status
cherrypy.config.update({'error_page.402': error_page_402})

在3.1版本中,除了使用编号错误代码外,您还可以提供error_page.default来处理所有没有自己的错误页面条目的代码。

未预期的错误

CherryPy还具有通用错误处理机制:每当您的代码出现未预期的错误时,它将调用Request.error_response来设置响应状态、标头和正文。默认情况下,这与HTTPError(500)相同。如果您想提供其他行为,则通常替换“request.error_response”。
以下是一些示例代码,演示如何显示自定义错误消息并发送包含错误的电子邮件。
from cherrypy import _cperror

def handle_error():
    cherrypy.response.status = 500
    cherrypy.response.body = [
        "<html><body>Sorry, an error occurred</body></html>"
    ]
    sendMail('error@domain.com',
             'Error in your web app',
             _cperror.format_exc())

@cherrypy.config(**{'request.error_response': handle_error})
class Root:
    pass

请注意,您必须明确设置response.body,而不仅仅返回错误消息作为结果。

谢谢 - 我已经好几年没有使用CherryPy了,所以我相信你说的是正确的 :) - WoJ

3

CherryPy确实捕获了您的异常。这就是它如何通过捕获异常向浏览器返回有效页面。

我建议您阅读所有文档。我知道它的文档不是最好的,也没有组织好,但如果您至少浏览一下,框架将更容易理解。这是一个小型框架,但几乎可以完成应用服务器的所有功能。

import cherrypy


def show_blank_page_on_error():
    """Instead of showing something useful to developers but
    disturbing to clients we will show a blank page.

    """
    cherrypy.response.status = 500

    cherrypy.response.body = ''


class Root():
    """Root of the application"""

    _cp_config = {'request.error_response': show_blank_page_on_error}

    @cherrypy.expose
    def index(self):
        """Root url handler"""

        raise Exception 

请参考上述页面文档中的示例此处以获取更多参考信息。


1
我查了谷歌,看到了解决方案。只是我没能让它起作用。如果你知道如何解决这个问题,相关的代码片段会很棒。 - WoJ
@WoJ StackOverflow社区不是为那些明显缺乏努力和理解主题的人提供救济的地方。然而,我感到慷慨。此外,我有点烦恼,你决定修改你的问题,并回答自己的新问题。现在,你已经接受了一个答案,并评论了一个处理GET参数并找到适当处理程序的解决方案,而不是捕获异常。这就是为什么一次提出多个问题通常也是一个坏主意的原因。你还在论坛中发布了两次相同的问题...这是不好的行为。 - Derek Litz
1
谢谢您的慷慨,我非常感激。首先:起初我提出了一个完整的问题,并附上了代码和不起作用的示例。我应该如何提问?缺少哪些研究?(这是指“明显缺乏努力”,恕我直言,您无法从屏幕后面判断)。第二:在您决定发布实际代码之前,没有任何答案回答了我的问题(这很好,别误会)。因此,在更多的研究之后,我找到了一种不使用CherryPy的解决方案,并修改了我的问题+添加了一些可行的方法。(接下来的部分将继续...) - WoJ
1
现在我看到了能够完美解决问题的可行代码,因此我将更改最佳答案(尽管它被踩了)。这就是所谓的“互帮互助”,也是像 SO 这样的平台的核心理念。 - WoJ
1
我确实发布了不工作的代码。如果您启动它并复制我在代码中添加的步骤,您会看到我的问题描述中所述的问题。关于问题被捕获并在浏览器中显示的评论是有用的——这正是我想要避免的(显示回溯)。 - WoJ
显示剩余6条评论

3

请选择最适合您的方式:默认方法自定义错误处理

我认为你不应该使用 BaseHTTPServer。如果您的应用程序如此简单,只需获取轻量级框架(例如 Flask),即使它可能有点过度,或者保持低水平但仍在WSGI标准范围内并使用符合WSGI的服务器。


2
您可以简单地使用try/except语句:
try:
    cherrypy.quickstart(MainServer())
except: #catches all errors, including basic python errors
    print("Error!")

这将捕获每一个错误。但如果你只想捕获 cherrypy._cperror

from cherrypy import _cperror

try:
    cherrypy.quickstart(MainServer())
except _cperror.CherryPyException: #catches only CherryPy errors.
    print("CherryPy error!")

希望这有所帮助!

当然啊 - 我没想过要加上 try, except。我更新了代码,但它不起作用(尝试访问 http://127.0.0.1:8080/abcde 时出现404和Python回溯指示 NotFound 异常)。 - WoJ
嗯...我认为不应该发生这种情况。这是你的完整功能代码吗? - aIKid
不,当然还有更多的代码(你看到 print params['a'] 的地方)。但是我放置的代码是完全可用的,包括缺少异常捕获。你还需要什么? - WoJ
这是我的完整代码。我删除了与我的问题无关的部分,这个脚本是一个正确工作的Python脚本。在那个脚本上,我想要捕捉异常。如果有帮助,假设我想要一个打印URL中参数a值的脚本,忽略任何异常(这就是我发布的代码)。 - WoJ
@WoJ 为什么要返回一个空页面?这会让用户和开发人员感到困惑。只需将服务器配置更改为“生产”而不是“开发”,即可获得默认错误页面而无需堆栈跟踪。如果您想实现自己的页面,请参阅我的答案中的文档。如果您想成为一名更好的开发人员,并且想要发明自己的抽象来解决新问题,那么理解其他人做得很好的抽象是一个很好的起点。 - Derek Litz
显示剩余5条评论

1
import cherrypy
from cherrypy import HTTPError


def handle_an_exception():
    cherrypy.response.status = 500
    cherrypy.response.headers['content-type'] = 'text/plain;charset=UTF-8'
    cherrypy.response.body = b'Internal Server Error'


def handle_a_404(status=None, message=None, version=None, traceback=None):
    cherrypy.response.headers['content-type'] = 'text/plain;charset=UTF-8'
    return f'Error page for 404'.encode('UTF-8')


def handle_default(status=None, message=None, version=None, traceback=None):
    cherrypy.response.headers['content-type'] = 'text/plain;charset=UTF-8'
    return f'Default error page: {status}'.encode('UTF-8')


class Root:
    """Root of the application"""
    _cp_config = {
        # handler for an unhandled exception
        'request.error_response': handle_an_exception,
        # specific handler for HTTP 404 error
        'error_page.404': handle_a_404,
        # default handler for any other HTTP error
        'error_page.default': handle_default
    }

    @cherrypy.expose
    def index(self):
        """Root url handler"""
        raise Exception("an exception")

    @cherrypy.expose
    def simulate400(self):
        raise HTTPError(status=400, message="Bad Things Happened")


cherrypy.quickstart(Root())

测试使用以下链接:

http://127.0.0.1:8080/

http://127.0.0.1:8080/simulate400

http://127.0.0.1:8080/missing


0

虽然这是我在搜索cherrypy异常处理时获得的顶部结果之一,但接受的答案并没有完全回答我的问题。以下是针对cherrypy 14.0.0的可工作代码:

# Implement handler method
def exception_handler(status, message, traceback, version)
    # Your logic goes here 

class MyClass()    

   # Update configurations
   _cp_config = {"error_page.default": exception_handler}

请注意方法签名。没有此签名,您的方法将无法被调用。以下是方法参数的内容:
  • status:HTTP状态和描述
  • message:附加到异常的消息
  • traceback:格式化的堆栈跟踪
  • version:Cherrypy版本

0
也许您可以使用 cherrypy.tools 中的 'before_error_response' 处理程序。
@cherrypy.tools.register('before_error_response', priority=90)
def handleexception():
    cherrypy.response.status = 500
    cherrypy.response.body = ''

而且别忘了启用它:

tools.handleexception.on = True

-2

我放弃了使用CherryPy,最终使用以下代码解决了问题,只需几行标准的BaseHTTPServer

from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer
from urlparse import urlparse, parse_qs

class GetHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        url = urlparse(self.path)
        d = parse_qs(url[4])
        if 'c' in d:
            print d['c'][0]
        self.send_response(200)
        self.end_headers()
        return

server = HTTPServer(('localhost', 8080), GetHandler)
server.serve_forever()

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接