Browse Source

Flask will now give you an error in debug mode if a post request caused a redirect by the routing system.

pull/302/head
Armin Ronacher 14 years ago
parent
commit
6847329134
  1. 20
      flask/app.py
  2. 26
      flask/debughelpers.py
  3. 24
      tests/flask_tests.py

20
flask/app.py

@ -18,7 +18,7 @@ from itertools import chain
from functools import update_wrapper
from werkzeug.datastructures import ImmutableDict
from werkzeug.routing import Map, Rule
from werkzeug.routing import Map, Rule, RequestRedirect
from werkzeug.exceptions import HTTPException, InternalServerError, \
MethodNotAllowed, BadRequest
@ -1134,6 +1134,22 @@ class Flask(_PackageBoundObject):
return InternalServerError()
return handler(e)
def raise_routing_exception(self, request):
"""Exceptions that are recording during routing are reraised with
this method. During debug we are not reraising redirect requests
for non ``GET``, ``HEAD``, or ``OPTIONS`` requests and we're raising
a different error instead to help debug situations.
:internal:
"""
if not self.debug \
or not isinstance(request.routing_exception, RequestRedirect) \
or request.method in ('GET', 'HEAD', 'OPTIONS'):
raise request.routing_exception
from .debughelpers import FormDataRoutingRedirect
raise FormDataRoutingRedirect(request)
def dispatch_request(self):
"""Does the request dispatching. Matches the URL and returns the
return value of the view or error handler. This does not have to
@ -1146,7 +1162,7 @@ class Flask(_PackageBoundObject):
"""
req = _request_ctx_stack.top.request
if req.routing_exception is not None:
raise req.routing_exception
self.raise_routing_exception(req)
rule = req.url_rule
# if we provide automatic options for this URL and the
# request came with the OPTIONS method, reply automatically

26
flask/debughelpers.py

@ -33,6 +33,32 @@ class DebugFilesKeyError(KeyError, AssertionError):
return self.msg
class FormDataRoutingRedirect(AssertionError):
"""This exception is raised by Flask in debug mode if it detects a
redirect caused by the routing system when the request method is not
GET, HEAD or OPTIONS. Reasoning: form data will be dropped.
"""
def __init__(self, request):
exc = request.routing_exception
buf = ['A request was sent to this URL (%s) but a redirect was '
'issued automatically by the routing system to "%s".'
% (request.url, exc.new_url)]
# In case just a slash was appended we can be extra helpful
if request.base_url + '/' == exc.new_url.split('?')[0]:
buf.append(' The URL was defined with a trailing slash so '
'Flask will automatically redirect to the URL '
'with the trailing slash if it was accessed '
'without one.')
buf.append(' Make sure to directly send your %s-request to this URL '
'since we can\'t make browsers or HTTP clients redirect '
'with form data.' % request.method)
buf.append('\n\nNote: this exception is only raised in debug mode')
AssertionError.__init__(self, ''.join(buf).encode('utf-8'))
def attach_enctype_error_multidict(request):
"""Since Flask 0.8 we're monkeypatching the files object in case a
request is detected that does not use multipart form data but the files

24
tests/flask_tests.py

@ -981,6 +981,30 @@ class BasicFunctionalityTestCase(unittest.TestCase):
self.assertEqual(got, [42])
self.assert_(app.got_first_request)
def test_routing_redirect_debugging(self):
app = flask.Flask(__name__)
app.debug = True
@app.route('/foo/', methods=['GET', 'POST'])
def foo():
return 'success'
with app.test_client() as c:
try:
c.post('/foo', data={})
except AssertionError, e:
self.assert_('http://localhost/foo/' in str(e))
self.assert_('Make sure to directly send your POST-request '
'to this URL' in str(e))
else:
self.fail('Expected exception')
rv = c.get('/foo', data={}, follow_redirects=True)
self.assertEqual(rv.data, 'success')
app.debug = False
with app.test_client() as c:
rv = c.post('/foo', data={}, follow_redirects=True)
self.assertEqual(rv.data, 'success')
class JSONTestCase(unittest.TestCase):

Loading…
Cancel
Save