Вопрос

I use CherryPy to run a very simple web server. It is intended to process the GET parameters and, if they are correct, do something with them.

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())

For example,

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

I am trying to catch this exception and show a blank page because the clients do not care about it. Specifically, the result would be an empty body, no matter the url or query string that resulted in an exception.

I had a look at documentation on error handling cherrypy._cperror, but I did not find a way to actually use it.

Note: I gave up using CherryPy and found a simple solution using BaseHTTPServer (see my answer below)

Это было полезно?

Решение

Docs somehow seem to miss this section. This is what I found while looking for detailed explanation for custom error handling from the source code.

Custom Error Handling

Anticipated HTTP responses

The 'error_page' config namespace can be used to provide custom HTML output for expected responses (like 404 Not Found). Supply a filename from which the output will be read. The contents will be interpolated with the values %(status)s, %(message)s, %(traceback)s, and %(version)s using plain old Python string formatting.

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

Beginning in version 3.1, you may also provide a function or other callable as an error_page entry. It will be passed the same status, message, traceback and version arguments that are interpolated into templates

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})

Also in 3.1, in addition to the numbered error codes, you may also supply error_page.default to handle all codes which do not have their own error_page entry.

Unanticipated errors

CherryPy also has a generic error handling mechanism: whenever an unanticipated error occurs in your code, it will call Request.error_response to set the response status, headers, and body. By default, this is the same output as HTTPError(500). If you want to provide some other behavior, you generally replace "request.error_response".

Here is some sample code that shows how to display a custom error message and send an e-mail containing the error

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

Note that you have to explicitly set response.body and not simply return an error message as a result.

Другие советы

Choose what's most suitable for you: Default Methods, Custom Error Handling.

I don't think you should use BaseHTTPServer. If your app is that simple, just get a lightweight framework (e. g. Flask), even though it might be a bit overkill, OR stay low level but still within the WSGI standard and use a WSGI-compliant server.

CherryPy IS catching your exception. That's how it returns a valid page to the browser with the caught exception.

I suggest you read through all the documentation. I realize it isn't the best documentation or organized well, but if you at least skim through it the framework will make more sense. It is a small framework, but does almost everything you'd expect from a application server.

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 

See this for the example in the documentation on the page mentioned above for further reference.

You can simply use a try/except clause:

try:
    cherrypy.quickstart(MainServer())
except: #catches all errors, including basic python errors
    print("Error!")

This will catch every single error. But if you want to catch only cherrypy._cperror:

from cherrypy import _cperror

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

Hope this helps!

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())

Test with:

http://127.0.0.1:8080/

http://127.0.0.1:8080/simulate400

http://127.0.0.1:8080/missing

Though this was the one of the top results when I searched for cherrypy exception handling, accepted answer did not fully answered the question. Following is a working code against 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}

Note the method signature. Without this signature your method will not get invoked.Following are the contents of method parameters,

  • status : HTTP status and a description
  • message : Message attached to the exception
  • traceback : Formatted stack trace
  • version : Cherrypy version

Maybe you could use a 'before_error_response' handler from cherrypy.tools

@cherrypy.tools.register('before_error_response', priority=90)
def handleexception():
    cherrypy.response.status = 500
    cherrypy.response.body = ''

And don't forget to enable it:

tools.handleexception.on = True

I gave up using CherryPy and ended up using the follwing code, which solves the issue in a few lines with the standard 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()
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top