在Flask中使用hidden field _method来更改请求方法 [英] Changing request method using hidden field _method in Flask

查看:433
本文介绍了在Flask中使用hidden field _method来更改请求方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

开始拿起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 =hiddenname =_ methodvalue =PUT>
< input class =span12name =emailtype =textplaceholder =E-mail addressvalue ={{email}}>
< input class =span12name =passwordtype =passwordplaceholder =Your password>
< div class =remember>
< input id =remember-metype =checkbox>
< label for =记住我>记住我< / label>
< / div>
< input class =btn-glow primary logintype =submitname =submitvalue =Log in>
< / form>

app / __ init __。py

<$ p $ (from gamasutra.com)from $ {code $ from $'$'$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $' _method'):
self.app = app
self.input_name = input_name
$ b $ def __call __(self,environ,start_response):
request = Request(environ)
$ b $如果request.form中的self.input_name:
method = request.form [self.input_name] .upper()

如果['GET' ,'POST','PUT','DELETE']:
environ ['REQUEST_METHOD'] =方法

返回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 /登录,方法= ['GET','POST','PUT'])
def login():
emailvalue ='test@test.com'
request.method ==' PUT':
emailvalue = request.form ['email']
return render_template('login.html',email = emailvalue)
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 的字符串中,并用<$ c替换 wsgi.input $ c> BytesIO(data),它有一个可以使用的 seek 方法。这样做会带来一些不利之处,最明显的就是所有上传的数据在传递到应用程序之前都保证完全读入内存。也许还有一些危险的边缘情况,我不知道我自己,这就是为什么我永远不会冒险尝试真正的应用程序中的以下内容:

 来自werkzeug.formparser从werkzeug.wsgi导入parse_form_data 
导入get_input_stream $ b $从io导入BytesIO

class MethodMiddleware(object) :
实际上不这样做,缺点不值得。
def __init __(self,app):
self.app = app

def __call __(self,environ,start_response):
如果是environ ['REQUEST_METHOD']。 \
BytesIO(get_input_stream(environ).read())
formdata = parse_form_data(environ)[1]
stream.seek(0)

method =如果方法在('GET','POST','PUT','DELETE()方法中,formdata.get('_ method','').upper ):
environ ['REQUEST_METHOD'] =方法

返回self.app(environ,start_response)


Started picking up Python and Flask as a learning exercise, and coming from PHP/Symfony2, I could add a hidden _method field to a form to override the POST method with either a DELETE or PUT.

It seems Flask doesn't support this natively, and I've been hacking around with various ideas including http://flask.pocoo.org/snippets/38/, which works, but involves putting the override in the form action, rather than as a hidden field, which IMO makes the URL look unsightly.

There is a snippet in the comments of the above address, which makes _method work from a routing perspective, but as discussed there as well, does cause the request to hang if you then try to access request.form in the views.

Does anyone have a workaround for this? If not, I'll just handle everything as POST, but would be nice to be able to find a way to get it to work.

Cheers.


EDIT: Here's the code for anyone who wants to take a look:

Template:

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

app/__init__.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

View:

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)

解决方案

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

def app(environ, start_response):
    start_response('200 OK', [('Content-Type', 'text/plain')])
    yield str(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

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)

这篇关于在Flask中使用hidden field _method来更改请求方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆