Почему и как http-метод текущей конечной точки влияет на поведение url_for во Flask?

Я использую:


Рассмотрим этот изолированный пример поведения, о котором я размышляю:

from flask import Flask, url_for, render_template_string

app = Flask(__name__)

@app.route('/hi/', methods=['POST'])
@app.route('/hi/<var>')
def hi(var):
    return ''

@app.route('/')
def index():
    return render_template_string('''
<html>
    <head>
        <title>GET or POST</title>
    </head>
    <body>
        <form action = "{{ url_for('path') }}">
            <input type='SUBMIT' value='GET'>
        </form>
        <form action = "{{ url_for('path') }}" method='POST'>
            <input type='SUBMIT' value='POST'>
        </form>
    </body>
</html>''')

@app.route('/path/', methods=['GET', 'POST'])
def path():
    return str(url_for('hi', var='Hello', var2='xyz'))

Чтобы прояснить свои намерения, я кратко опишу, что происходит и что я пытаюсь понять:

  1. Конечная точка /hi/ имеет 'необязательный' параметр (<var>), который принимается только через запрос GET. «Обычная» (то есть без аргументов) конечная точка /hi/ доступна только через метод POST.
  2. К конечной точке /path/ можно получить доступ как через HTTP-методы GET, так и POST. И он просто возвращает путь для hi, сгенерированного через url_for('hi', var='Hello', var2='xyz').
  3. Теперь я ожидаю, что /path/ вернет ту же строку, независимо от того, какой метод использовался для доступа к ней (GET или POST). Но это не так: для GET он возвращает /hi/Hello?var2=xyz (как я, собственно, и ожидал), но для POST я получаю /hi/?var=Hello&var2=xyz (что кажется мне странным поведением).

Путем испытаний и ошибок я смог выяснить, что добавление POST к методам, разрешенным для /hi/<var>, решает проблему (/hi/Hello?var2=xyz возвращается /path/ как для GET, так и для POST), то есть:

@app.route('/hi/', methods=['POST'])
@app.route('/hi/<var>', methods=['GET', 'POST'])
def hi(var):
    ...

Надеюсь, кто-нибудь сможет объяснить мне следующее:

  1. Почему это происходит (/path/ возвращает разные значения для POST и GET)?
  2. Можно ли избежать такого поведения, не разрешив POST на /hi/<var>?

🤔 А знаете ли вы, что...
Python подходит для начинающих программистов благодаря своей простоте и читаемости кода.


166
1

Ответ:

На ответы наткнулся благодаря Другой вопрос =)


Отвечая на мои собственные вопросы:

  1. (не уверен на 100%, был бы признателен, если бы кто-нибудь подтвердил, что я здесь прав) url_for имеет необязательный параметр _method, который по умолчанию соответствует методу, который использовался для возврата представления Текущий. Итак, /path/ действительно возвращает return str(url_for('hi', var='Hello', var2='xyz', _method='GET'), когда к нему обращались через GET-запрос, и return str(url_for('hi', var='Hello', var2='xyz', _method='POST'), если к нему обращались через POST-запрос. Вот почему разрешение POST на обеих конечных точках (/hi/<var> и /hi/) решает проблему - если POST разрешен только для /hi/, то return str(url_for('hi', var='Hello', var2='xyz', _method='POST') проверяет, известен ли var только /hi/ (а он, очевидно, не известен). С другой стороны, если POST разрешен на обеих конечных точках, то /hi/ и /hi/<var> проверяются на наличие var, и /hi/<var> выбран правильно.

  2. Учитывая предыдущий пункт, исправление теперь довольно очевидно: return str(url_for('hi', var='Hello', var2='xyz', _method='GET') следует заменить на return str(url_for('hi', var='Hello', var2='xyz') в исходном фрагменте.