Add new exception tornado.web.Finish to quietly end a request.
This allows error pages to be generated inline with the main code instead of in write_error and is friendlier to generating error pages from library code. Closes #1064.
This commit is contained in:
parent
2cec3195f2
commit
596038c284
|
@ -235,6 +235,7 @@
|
||||||
Everything else
|
Everything else
|
||||||
---------------
|
---------------
|
||||||
.. autoexception:: HTTPError
|
.. autoexception:: HTTPError
|
||||||
|
.. autoexception:: Finish
|
||||||
.. autoexception:: MissingArgumentError
|
.. autoexception:: MissingArgumentError
|
||||||
.. autoclass:: UIModule
|
.. autoclass:: UIModule
|
||||||
:members:
|
:members:
|
||||||
|
|
|
@ -10,7 +10,7 @@ from tornado.template import DictLoader
|
||||||
from tornado.testing import AsyncHTTPTestCase, ExpectLog, gen_test
|
from tornado.testing import AsyncHTTPTestCase, ExpectLog, gen_test
|
||||||
from tornado.test.util import unittest
|
from tornado.test.util import unittest
|
||||||
from tornado.util import u, bytes_type, ObjectDict, unicode_type
|
from tornado.util import u, bytes_type, ObjectDict, unicode_type
|
||||||
from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature_v1, create_signed_value, decode_signed_value, ErrorHandler, UIModule, MissingArgumentError, stream_request_body
|
from tornado.web import RequestHandler, authenticated, Application, asynchronous, url, HTTPError, StaticFileHandler, _create_signature_v1, create_signed_value, decode_signed_value, ErrorHandler, UIModule, MissingArgumentError, stream_request_body, Finish
|
||||||
|
|
||||||
import binascii
|
import binascii
|
||||||
import contextlib
|
import contextlib
|
||||||
|
@ -2307,3 +2307,20 @@ class XSRFTest(SimpleHandlerTestCase):
|
||||||
body=urllib_parse.urlencode(dict(_xsrf=body_token)),
|
body=urllib_parse.urlencode(dict(_xsrf=body_token)),
|
||||||
headers=self.cookie_headers(cookie_token))
|
headers=self.cookie_headers(cookie_token))
|
||||||
self.assertEqual(response.code, 200)
|
self.assertEqual(response.code, 200)
|
||||||
|
|
||||||
|
|
||||||
|
@wsgi_safe
|
||||||
|
class FinishExceptionTest(SimpleHandlerTestCase):
|
||||||
|
class Handler(RequestHandler):
|
||||||
|
def get(self):
|
||||||
|
self.set_status(401)
|
||||||
|
self.set_header('WWW-Authenticate', 'Basic realm="something"')
|
||||||
|
self.write('authentication required')
|
||||||
|
raise Finish()
|
||||||
|
|
||||||
|
def test_finish_exception(self):
|
||||||
|
response = self.fetch('/')
|
||||||
|
self.assertEqual(response.code, 401)
|
||||||
|
self.assertEqual('Basic realm="something"',
|
||||||
|
response.headers.get('WWW-Authenticate'))
|
||||||
|
self.assertEqual(b'authentication required', response.body)
|
||||||
|
|
|
@ -1384,6 +1384,11 @@ class RequestHandler(object):
|
||||||
" (" + self.request.remote_ip + ")"
|
" (" + self.request.remote_ip + ")"
|
||||||
|
|
||||||
def _handle_request_exception(self, e):
|
def _handle_request_exception(self, e):
|
||||||
|
if isinstance(e, Finish):
|
||||||
|
# Not an error; just finish the request without logging.
|
||||||
|
if not self._finished:
|
||||||
|
self.finish()
|
||||||
|
return
|
||||||
self.log_exception(*sys.exc_info())
|
self.log_exception(*sys.exc_info())
|
||||||
if self._finished:
|
if self._finished:
|
||||||
# Extra errors after the request has been finished should
|
# Extra errors after the request has been finished should
|
||||||
|
@ -1938,6 +1943,9 @@ class HTTPError(Exception):
|
||||||
`RequestHandler.send_error` since it automatically ends the
|
`RequestHandler.send_error` since it automatically ends the
|
||||||
current function.
|
current function.
|
||||||
|
|
||||||
|
To customize the response sent with an `HTTPError`, override
|
||||||
|
`RequestHandler.write_error`.
|
||||||
|
|
||||||
:arg int status_code: HTTP status code. Must be listed in
|
:arg int status_code: HTTP status code. Must be listed in
|
||||||
`httplib.responses <http.client.responses>` unless the ``reason``
|
`httplib.responses <http.client.responses>` unless the ``reason``
|
||||||
keyword argument is given.
|
keyword argument is given.
|
||||||
|
@ -1966,6 +1974,25 @@ class HTTPError(Exception):
|
||||||
return message
|
return message
|
||||||
|
|
||||||
|
|
||||||
|
class Finish(Exception):
|
||||||
|
"""An exception that ends the request without producing an error response.
|
||||||
|
|
||||||
|
When `Finish` is raised in a `RequestHandler`, the request will end
|
||||||
|
(calling `RequestHandler.finish` if it hasn't already been called),
|
||||||
|
but the outgoing response will not be modified and the error-handling
|
||||||
|
methods (including `RequestHandler.write_error`) will not be called.
|
||||||
|
|
||||||
|
This can be a more convenient way to implement custom error pages
|
||||||
|
than overriding ``write_error`` (especially in library code)::
|
||||||
|
|
||||||
|
if self.current_user is None:
|
||||||
|
self.set_status(401)
|
||||||
|
self.set_header('WWW-Authenticate', 'Basic realm="something"')
|
||||||
|
raise Finish()
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class MissingArgumentError(HTTPError):
|
class MissingArgumentError(HTTPError):
|
||||||
"""Exception raised by `RequestHandler.get_argument`.
|
"""Exception raised by `RequestHandler.get_argument`.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue