在Flask中使用隐藏字段_method更改请求方法

时间:2013-06-21 11:16:54

标签: python flask werkzeug

开始学习Python和Flask作为学习练习,来自PHP / Symfony2,我可以在表单中添加一个隐藏的_method字段,用DELETE或PUT覆盖POST方法。

似乎Flask本身不支持这种做法,而且我一直在讨论包括http://flask.pocoo.org/snippets/38/在内的各种想法,这些想法有效,但涉及将覆盖放在表单动作中,而不是作为隐藏字段, IMO使URL看起来不美观。

上述地址的注释中有一个片段,它使_method从路由角度工作,但正如在那里讨论的那样,如果您尝试访问视图中的request.form,则会导致请求挂起。

有没有人有解决方法?如果没有,我会把所有东西都当作POST来处理,但能够找到一种让它工作的方法会很好。

干杯。


编辑:以下是任何想要观看的人的代码:

模板:

<form action="{{ url_for('login') }}" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input class="span12" name="email" type="text" placeholder="E-mail address" value="{{ email }}">
    <input class="span12" name="password" type="password" placeholder="Your password">
    <a href="{{ url_for('reset_password') }}" class="forgot">Forgot password?</a>
    <div class="remember">
        <input id="remember-me" type="checkbox">
        <label for="remember-me">Remember me</label>
    </div>
    <input class="btn-glow primary login" type="submit" name="submit" value="Log in">
</form>

应用程序/ __初始化__。PY

from flask import Flask
from werkzeug.wrappers import Request

class MethodRewriteMiddleware(object):
    def __init__(self, app, input_name='_method'):
        self.app = app
        self.input_name = input_name

    def __call__(self, environ, start_response):
        request = Request(environ)

        if self.input_name in request.form:
            method = request.form[self.input_name].upper()

            if method in ['GET', 'POST', 'PUT', 'DELETE']:
                environ['REQUEST_METHOD'] = method

        return self.app(environ, start_response)

app = Flask(__name__)
app.wsgi_app = MethodRewriteMiddleware(app.wsgi_app)
from app import views

查看:

from flask import render_template
@app.route('/user/login', methods=['GET','POST','PUT'])
def login():
    emailvalue = 'test@test.com'
    if request.method == 'PUT':
        emailvalue = request.form['email']
    return render_template('login.html', email=emailvalue)

2 个答案:

答案 0 :(得分:4)

正如您已经指出的那样,您的中间件使后来的request.form为空。这是因为request.form正在从类似文件的对象中读取。引用PEP 333

  

wsgi.input - 可以从中读取HTTP请求正文的输入流(类文件对象)。 (服务器或网关可以按照应用程序的请求执行按需读取,或者它可以预先读取客户端的请求主体并将其缓冲在内存中或磁盘上,或者使用任何其他技术来提供这样的输入流,它的偏好。)

请注意,本段并未告诉我们这个“类文件对象”是否会提供将指针重置为文件开头的任何可能性。事实上,如果我们尝试以下应用程序:

from werkzeug.serving import run_simple

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield str(dir(environ['wsgi.input']))

run_simple('localhost', 5000, app)

它不显示此文件对象具有seek方法的任何索引。

所以,可以做什么将所有内容读入名为data的字节字符串中,并将wsgi.input替换为BytesIO(data)seek具有from werkzeug.formparser import parse_form_data from werkzeug.wsgi import get_input_stream from io import BytesIO class MethodMiddleware(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) 可以使用的方法。 这样做会带来一些缺点,最明显的是所有上传的数据都保证在传递给应用程序之前完全读入内存。可能还有一些我不了解的危险边缘情况,这就是为什么我永远不会冒着在实际应用程序中尝试以下内容的风险:

{{1}}

答案 1 :(得分:1)

您可以使用MethodView中的flask.views并将其发送到正确的方法。我已经创建了一个简单的Flask App来演示它。

from flask import Flask, jsonify, request
from flask.views import MethodView

app = Flask(__name__)

class MyView(MethodView):

    def get(self):
        return jsonify({'method': 'GET'})

    def post(self):
        method = request.form.get('_method', 'POST')
        if method == 'POST':
            return jsonify({'method':method})
        else:
            if hasattr(self, method.lower()):            
                return getattr(self, method.lower())()
            else:
                return jsonify({'method': 'UNKNOWN'})

    def put(self):
        return jsonify({'method': 'PUT'})

    def delete(self):
        return jsonify({'method': 'DELETE'})

    def create(self):
        # NOT A HTTP VERB
        return jsonify({'method': 'CREATE'})

app.add_url_rule('/', view_func=MyView.as_view('myview'))

if __name__ == "__main__":
    app.run(debug=True)