Мое решение состоит в том, чтобы расширить класс Flask-RESTful Api
и реализовать собственный обработчик ошибок. официальная документация немного объясняет о расширении, но не вдаваться в достаточные подробности.
Пользовательское сообщение об ошибке
Все ошибки в моем приложении находятся в этой структуре
class InvalidParameter(Exception):
status_code = 400
def __init__(self, message, status_code=None, resource=None,
field=None, code=None, stack_trace=None):
Exception.__init__(self)
self.message = message
if status_code is not None:
self.status_code = status_code
self.resource = resource
self.field = field
self.code = code
self.stack_trace = stack_trace
def to_dict(self):
rv = {}
errors = {}
rv['message'] = self.message
errors['resource'] = self.resource
errors['field'] = self.field
errors['code'] = self.code
errors['stack_trace'] = self.stack_trace
rv['errors'] = errors
return rv
Это то, что Flask-RESTful возвращает по умолчанию
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
Вывод должен быть JSON в этом формате
{
"message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. You have requested this URI [/projects/1asd] but did you mean /projects/<int:project_id> or /projects or /project/new ?",
"errors": {
"resource": null,
"field": null,
"code": "invalid_url",
"stack_trace": null
}
}
Решение
Расширьте класс Api
и перезапишите метод handle_error
для 404
ошибок.
class CustomApi(Api):
def handle_error(self, e):
code = getattr(e, 'code', 404)
if code == 404:
response_dict = Api.handle_error(self, e).__dict__
resp_message = response_dict['response'][0]
error_message = re.sub(r'.*"(.*)".*', r'\1', resp_message.decode('utf-8'), flags=re.DOTALL)
err = InvalidParameter(error_message, stack_trace=None,
resource=None, code="invalid_url", field=None)
return self.make_response(err.to_dict(), 404) #{'message': "something", 'error': {'sub1': 'val1'}}, 404)
return super(Api, self).handle_error(e)
Словарь handle_error
в этом формате
{'headers': Headers([('Content-Type', 'application/json'), ('Content-Length', '263')]), '_status_code': 404, '_status': '404 NOT FOUND', 'direct_passthrough': False, '_on_close': [], 'response': [b'{\n "message": "The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again. You have requested this URI [/projects/1asd] but did you mean /projects/<int:project_id> or /projects or /project/new ?"\n}\n']}
Я хочу повторно использовать сгенерированный 'response':'message'
, но не в формате по умолчанию. message
не был правильно отформатирован JSON, поэтому я удаляю все, кроме содержимого message
, используя это регулярное выражение
error_message = re.sub(r'.*"(.*)".*', r'\1', resp_message.decode('utf-8'), flags=re.DOTALL)
Обратите внимание, что re.DOTALL
необходимо для удаления \n
, добавленного Flask-RESTful.
Код, который на самом деле формирует ответ JSON, — self.make_response(err.to_dict(), 404)
.
Для всех других ошибок, отличных от 404 (например, 400, 500, 503), ошибка просто передается в исходный класс Flask-RESTful Api.
Обратите внимание, что при создании приложения Flask вам необходимо использовать собственный класс API и отловить все ошибки 404:
app = Flask(__name__)
api = CustomApi(app, catch_all_404s=True)
person
Hanxue
schedule
30.08.2017
project_id
как сint
и перенести проверку параметров запроса на другой уровень (из поля зрения)? - person Danila Ganchar   schedule 24.08.2017project_id
этоint
. Если это не так, я хочу вернуть пользовательское сообщение об ошибке JSON. - person Hanxue   schedule 24.08.2017flask_restful
в валидатор добавлена поддержка . Вы можете использовать его в будущем. - person Danila Ganchar   schedule 08.09.2017