10

学習課題として Python と Flask を取り上げ始め、PHP/Symfony2 から来て、非表示の _method フィールドをフォームに追加して、POST メソッドを DELETE または PUT でオーバーライドすることができました。

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>

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

意見:

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

2 に答える 2

5

すでに指摘したように、ミドルウェアは後者を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このファイル オブジェクトにメソッドがあるインデックスは表示されません。

したがって、すべてを と呼ばれるバイト文字列に読み込み、 に置き換えることができます。これには、使用できるメソッドがあります。これを行うと、いくつかの欠点が生じます。最も明白なのは、アップロードされたすべてのデータが、アプリケーションに渡される前に完全にメモリに読み込まれることが保証されていることです。おそらく、私が知らない危険なエッジケースもいくつかあります。そのため、実際のアプリケーションで次のことを試すリスクは決してありません。datawsgi.inputBytesIO(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)
于 2013-07-24T08:02:26.070 に答える
1

MethodViewfromを使用しflask.viewsて、適切なメソッドにディスパッチできます。それを示すために、単純な Flask アプリを作成しました。

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)
于 2013-07-19T15:37:28.163 に答える