Changing Request Method Using Hidden Field _method In Flask
Solution 1:
As you already pointed out, your middleware makes the later request.form
empty. This is because request.form
is reading from a file-like object. Quoting PEP 333:
wsgi.input -- An input stream (file-like object) from which the HTTP request body can be read. (The server or gateway may perform reads on-demand as requested by the application, or it may pre- read the client's request body and buffer it in-memory or on disk, or use any other technique for providing such an input stream, according to its preference.)
Note that this paragraph doesn't tell us if this "file-like object" will provide any possibility to reset the pointer to the beginning of the file. In fact, if we try the following application:
from werkzeug.serving import run_simple
defapp(environ, start_response):
start_response('200 OK', [('Content-Type', 'text/plain')])
yieldstr(dir(environ['wsgi.input']))
run_simple('localhost', 5000, app)
It doesn't show any indices that this file object has a seek
method.
So, what you could do is read everything into a bytestring called data
, and replace wsgi.input
with BytesIO(data)
, which does have a seek
method one can use. Doing this brings several disadvantages with it, the most obvious being that all uploaded data is guaranteed to get completely read into memory before passing it to the application. Probably there are also some dangerous edge cases that i don't know myself of, which is why i never would risk trying the following in a real application:
from werkzeug.formparser import parse_form_data
from werkzeug.wsgi import get_input_stream
from io import BytesIO
classMethodMiddleware(object):
"""Don't actually do this. The disadvantages are not worth it."""def__init__(self, app):
self.app = app
def__call__(self, environ, start_response):
if environ['REQUEST_METHOD'].upper() == 'POST':
environ['wsgi.input'] = stream = \
BytesIO(get_input_stream(environ).read())
formdata = parse_form_data(environ)[1]
stream.seek(0)
method = formdata.get('_method', '').upper()
if method in ('GET', 'POST', 'PUT', 'DELETE'):
environ['REQUEST_METHOD'] = method
return self.app(environ, start_response)
Solution 2:
You could use the MethodView
from flask.views
and dispatch it to the right methods. I have created a simple Flask App to demonstrate it.
from flask import Flask, jsonify, request
from flask.views import MethodView
app = Flask(__name__)
classMyView(MethodView):
defget(self):
return jsonify({'method': 'GET'})
defpost(self):
method = request.form.get('_method', 'POST')
if method == 'POST':
return jsonify({'method':method})
else:
ifhasattr(self, method.lower()):
returngetattr(self, method.lower())()
else:
return jsonify({'method': 'UNKNOWN'})
defput(self):
return jsonify({'method': 'PUT'})
defdelete(self):
return jsonify({'method': 'DELETE'})
defcreate(self):
# NOT A HTTP VERBreturn jsonify({'method': 'CREATE'})
app.add_url_rule('/', view_func=MyView.as_view('myview'))
if __name__ == "__main__":
app.run(debug=True)
Post a Comment for "Changing Request Method Using Hidden Field _method In Flask"