forked from orbit-oss/flask
Flask will now give you an error in debug mode if a post request caused a redirect by the routing system.
This commit is contained in:
parent
ce70131975
commit
6847329134
3 changed files with 68 additions and 2 deletions
20
flask/app.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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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…
Add table
Add a link
Reference in a new issue