私はFlask-WTFを使用しています:
これが私のフォームです:
from flask.ext.wtf import Form, TextField
class BookNewForm(Form):
name = TextField('Name')
これがコントローラです:
@book.route('/book/new', methods=['GET', 'POST'])
def customers_new():
form = BookNewForm()
if form.is_submitted():
print "submitted"
if form.validate():
print "valid"
if form.validate_on_submit():
flash("Successfully created a new book")
return redirect(url_for('.books_show'))
return render_template('views/books_new.html', form=form)
ここで問題は、私の印刷ステートメントを見ると、常に送信されて印刷されますが、決して有効なものは印刷されず、validate_on_submit()が実行されないことです。どうして?
HTMLフォームにCSRFフィールドを挿入していません。
_<form method=post>
{{ form.csrf_token }}
{{ form.name }}
<input type=submit>
</form>
_
テンプレートに_form.csrf_token
_を追加した後( docs )、フォームは期待どおりに検証されます。
フォームを検証した後にprint(form.errors)
を追加して、発生したエラーを確認します。 errors
は検証前は空です。この場合、欠落に関するエラーがあります
_@book.route('/book/new_no_csrf', methods=['GET', 'POST'])
def customers_new_no_csrf():
form = BookNewForm()
print(form.errors)
if form.is_submitted():
print "submitted"
if form.validate():
print "valid"
print(form.errors)
if form.validate_on_submit():
flash("Successfully created a new book")
return redirect(url_for('.books_show'))
return render_template('books_new.html', form=form)
_
_{}
submitted
{'csrf_token': [u'CSRF token missing']}
127.0.0.1 - - [29/May/2012 02:01:08] "POST /book/new_no_csrf HTTP/1.1" 200 -
127.0.0.1 - - [29/May/2012 02:01:08] "GET /favicon.ico HTTP/1.1" 404 -
_
エラーを印刷できます
print form.errors
または
app.logger.debug(form.errors)
また、csrf-errorが発生した場合は、テンプレートにform.csrf_tokenを設定する必要があります。
これをテンプレートhtmlファイルのタグの後に挿入します。
{{ form.csrf_token }}
テンプレートでFormField
をFieldList
に対して反復処理しようとすると、これに遭遇しました。 2つのhidden_tag要素を1つはFieldList
フォーム用に、もう1つはFieldForm
フォーム用に埋め込む必要がありました。テンプレートのコメントでキーワード「非表示タグ」を検索してください
class ParamRangeForm( FlaskForm ):
minX = FloatField( )
maxX = FloatField( )
class ParamRangesForm( FlaskForm ):
paramRanges = FieldList( FormField( ParamRangeForm ) )
submit = SubmitField( 'Submit' )
def loadParams( self ) :
for paramName in ["p1" , "p2" , "p3", "p4"] :
prf = ParamRangeForm( )
prf.minX = -100.9#float('-inf')
prf.maxX = 100.5#float('-inf')
self.paramRanges.append_entry( prf )
...
<form action="" method="POST" enctype="multipart/form-data">
{{ rangesForm.hidden_tag() }} <!--#### HIDDEN TAG #1 -->
<table>
<!--Print Column Headers-->
<thead>
<tr>
<th class="ColumnHeader">Parameter</td>
<th class="ColumnHeader">Min</td>
<th class="ColumnHeader">Max</td>
</tr>
</thead>
<!--Print Parameter Rows-->
<tbody>
{% for paramRange in rangesForm.paramRanges %}
<tr>
{{ paramRange.hidden_tag() }} <!--#### HIDDEN TAG #2 -->
<td>p{{ loop.index }}</td>
<td>{{ paramRange.minX }}</td>
<td>{{ paramRange.maxX }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{{ rangesForm.submit() }}
</form>
すべてのリクエストの前にログインしていなかった場合、flaskセッションをクリアしていました。これがこの問題の原因でした。
@main.before_request
def before_request():
if not current_user.is_authenticated():
# TODO clean sessions may cause CSRF missing issue
session.clear()
print "Session Cleared"
return redirect(url_for('auth.login'))
Flask-WTFでの検証問題のデバッグに数時間を費やしました。他の多くの問題と同様に、CSRF検証の問題でした。しかし、私のものは私が見つけた一般的な問題のいずれかによって引き起こされたのではありません。
CSRFの標準のFlask-WTF実装では、ブラウザにtwoを配信する必要があります。
One:非表示のCSRFフォームフィールド。
_<input id="csrf_token" name="csrf_token" type="hidden" value="ImYzODdmZTdhYTRlMmNkYWRjYmRlYWFmZjQxMDllZTQ1OWZmYzg3MTki.XKvOPg.gUCkF9j-vg0PrL2PRH-v43GeHu0">
_
2:セッションCookie HTTP応答ヘッダー。
_Set-Cookie: session=eyJjc3JmX3Rva2VuIjoiZjM4N2ZlN2FhNGUyY2RhZGNiZGVhYWZmNDEwOWVlNDU5ZmZjODcxOSJ9.XKvOPg.a3-W62MHvaGVkv2GYCi-dgpLE3Y; HttpOnly; Path=/
_
これらのいずれかがない場合、ブラウザは適切なCSRF検証の送信に失敗します。もちろん、これによりフォームの検証が失敗します。
_csrf_token
_非表示フィールドがフォームに存在するが、セッションCookieが欠落している場合、フォームが送信されると次の応答を受け取ります...
_Bad Request
The CSRF session token is missing.
_
私の場合、コードのバグのため、セッションCookieがありませんでした。 Flaskサイト全体にカスタムHTTPヘッダーを提供する必要がありました。このように含めました...
_class LocalFlask(Flask):
def process_response(self, response):
response.headers['my-header'] = 'My Header Value'
return response
app = LocalFlask(__name__)
_
ただし、これにより、_Flask.response.headers
_メソッドに依存するものはすべて失敗します。それらの1つは、セッションCookie HTTPヘッダーを設定するFlaks-WTFです。
これは、LocalFlask
クラスからメソッドを継承するようにFlask
クラスにsuper()
メソッドを追加することで解決できます。
_class LocalFlask(Flask):
def process_response(self, response):
response.headers['my-header'] = 'My Header Value'
#LocalFlask inherits methods from Flask
super(LocalFlask, self).process_response(response)
return response
app = LocalFlask(__name__)
_
APIが変更されたと思います。変更してみてください
from flask.ext.wtf import Form
に:
from flask_wtf import Form