Python 使用Flask中的隐藏字段_方法更改请求方法

Python 使用Flask中的隐藏字段_方法更改请求方法,python,flask,werkzeug,Python,Flask,Werkzeug,作为一个学习练习,我开始学习Python和Flask,从PHP/Symfony2开始,我可以在表单中添加一个隐藏的方法字段,用DELETE或PUT覆盖POST方法 Flask似乎不支持这一点,我一直在尝试各种想法,包括,这是可行的,但涉及到将覆盖放在表单操作中,而不是作为一个隐藏字段,这让URL看起来很难看 上面地址的注释中有一个片段,它使_方法从路由角度工作,但正如这里所讨论的,如果您尝试访问视图中的request.form,则确实会导致请求挂起 有人对此有解决办法吗?如果不是的话,我会把所有

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

Flask似乎不支持这一点,我一直在尝试各种想法,包括,这是可行的,但涉及到将覆盖放在表单操作中,而不是作为一个隐藏字段,这让URL看起来很难看

上面地址的注释中有一个片段,它使_方法从路由角度工作,但正如这里所讨论的,如果您尝试访问视图中的request.form,则确实会导致请求挂起

有人对此有解决办法吗?如果不是的话,我会把所有的事情都当作帖子来处理,但如果能找到一种方法让它发挥作用,那就太好了

干杯


编辑:以下代码供任何想要查看的人使用:

模板:

<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>
视图:


您可以使用
flask.views
中的
MethodView
并将其分派给正确的方法。我创建了一个简单的烧瓶应用程序来演示它

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)

正如您已经指出的,您的中间件使后面的
request.form
为空。这是因为
request.form
正在从类似文件的对象中读取。引述:

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)
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)
它不显示此文件对象具有
seek
方法的任何索引

因此,您可以将所有内容读入一个名为
data
的bytestring,并将
wsgi.input
替换为
BytesIO(data)
,它确实有一个可以使用的
seek
方法这样做会带来一些缺点,最明显的是,所有上传的数据在传递给应用程序之前都会被完全读取到内存中。可能还有一些我自己都不知道的危险边缘案例,这就是为什么我从不冒险在实际应用程序中尝试以下内容:

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)
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)

也许对你有用。嗨@johnthexiii,谢谢你的评论。实际上,我已经在使用request.form检查是否设置了_方法,但一旦我这样做并在中间件中更改了request_方法,我就无法访问视图中的request.form(应用程序只是挂起)。这是我尝试的建议的链接:(第二条评论)我已经编写了一些代码,以便更好地概述我试图实现的目标。如果您乐于从表单字段检查方法,这是否有助于解决将所有方法路由到端点的问题<代码>方法=request.form.get(“U方法”,request.method);if method=='PUT':…谢谢@DazWorrall,您可以这样做,但我想让该方法渗透到整个框架中,并为每个HTTP谓词提供单独的视图函数和路由修饰符,以实现可维护性,使函数保持简单,并且不必依赖if逻辑来路由请求。如果可能的话,我希望框架能够完成所有的路由。谢谢Markus,似乎就是这样。Django本身似乎也不支持它。虽然TastyPie不支持(不使用_方法,而是使用头覆盖),但可以对表单进行调整。目前,我只是使用表单中的post,并在Flask视图中执行PUT/post/DELETE逻辑。没有能够使用路由装饰器那么优雅,但必须这样做。谢谢你的意见。