Направляйте запросы на основе заголовка Accept во Flask
Я хотел бы направить к другим представлениям Flask на основе HTTP-заголовка Accept, например:
@api.route('/test', accept='text/html')
def test_html():
return "<html><body>Test</body></html>"
@api.route('/test', accept='text/json')
def test_json():
return jsonify(test="Test")
Я не нашел соответствующей опции в конструкторе правил Werkzeug, который используется Flask. Это отсутствующая функция или возможно достичь того же эффекта по-разному, например, перехватывая и изменяя URL-путь перед маршрутизацией?
Я не хочу объединять представления в одно, потому что это значительно усложнит код, их много, и они находятся в разных чертежах.
Я знаю, что подобный вопрос был задан, но никто не ответил на него, используя Flask. Это можно сделать в разных веб-фреймворках, например в Pyramid с использованием предикатов - пример кода можно найти в этом ответе.
2 ответа
Я написал декоратор, который делает это (копирование здесь для потомков). Это просто грубая идея, которая может быть улучшена в дальнейшем (например, возвращение 406 Not Acceptable
ответ вместо использования обработчика по умолчанию, когда нет обработчиков, соответствующих данному типу MIME). Больше объяснений в комментариях.
import functools
from flask import Flask, request, jsonify
app = Flask(__name__)
def accept(func_or_mimetype=None):
"""Decorator which allows to use multiple MIME type handlers for a single
endpoint.
"""
# Default MIME type.
mimetype = 'text/html'
class Accept(object):
def __init__(self, func):
self.default_mimetype = mimetype
self.accept_handlers = {mimetype: func}
functools.update_wrapper(self, func)
def __call__(self, *args, **kwargs):
default = self.default_mimetype
mimetypes = request.accept_mimetypes
best = mimetypes.best_match(self.accept_handlers.keys(), default)
# In case of Accept: */*, choose default handler.
if best != default and mimetypes[best] == mimetypes[default]:
best = default
return self.accept_handlers[best](*args, **kwargs)
def accept(self, mimetype):
"""Register a MIME type handler."""
def decorator(func):
self.accept_handlers[mimetype] = func
return func
return decorator
# If decorator is called without argument list, return Accept instance.
if callable(func_or_mimetype):
return Accept(func_or_mimetype)
# Otherwise set new MIME type (if provided) and let Accept act as a
# decorator.
if func_or_mimetype is not None:
mimetype = func_or_mimetype
return Accept
@app.route('/')
@accept # Or: @accept('text/html')
def index():
return '<strong>foobar</strong>'
@index.accept('application/json')
def index_json():
return jsonify(foobar=True)
@index.accept('text/plain')
def index_text():
return 'foobar\n', 200, {'Content-Type': 'text/plain'}
Я знаю, что это старый вопрос, но в итоге я искал что-то подобное, поэтому надеюсь, что это поможет кому-то еще.
Функция flask_accept позволяет обрабатывать различные типы Accept по разным маршрутам.
from flask import Flask, jsonify
from flask_accept import accept
app = Flask(__name__)
@app.route('/')
@accept('text/html')
def hello_world():
return 'Hello World!'
@hello_world.support('application/json')
def hello_world_json():
return jsonify(result="Hello World!")
if __name__ == '__main__':
app.run()
если вы просто хотите отклонить запросы в зависимости от того, являются ли они определенным типом данных, вы также можете использовать Flask-Negotiate
from flask import Flask
from flask_negotiate import consumes, produces
app = Flask(__name__)
@app.route('/consumes_json_only')
@consumes('application/json')
def consumes_json_only():
return 'consumes json only'
Когда кто-то пытается получить доступ к конечной точке без действительного заголовка Accept:
$ curl localhost:5000 -I
HTTP 415 (Unsupported Media Type)
Вы можете возвращать разные типы ответов на основе заголовка Accept с помощью запроса. Пример.
if request.accept_mimetypes['application/json']:
return jsonify(<object>), '200 OK'