• en
  • Language: ru
  • Documentation version: 1.1.x

Реализация исключений API

Реализация RESTful API поверх Flask - очень распространенное явление. Одна из первых вещей, с которой сталкиваются разработчики, - это осознание того, что встроенные исключения недостаточно выразительны для API и что тип содержимого text/html, который они выдают, не очень полезен для потребителей API.

Лучшим решением, чем использование abort для сигнализации об ошибках при неправильном использовании API, является реализация собственного типа исключения и установка обработчика ошибок для него, который выдает ошибки в том формате, который ожидает пользователь.

Простой класс исключений

Основная идея заключается в том, чтобы ввести новое исключение, которое может принимать соответствующее человекочитаемое сообщение, код статуса ошибки и некоторую необязательную полезную нагрузку, чтобы дать больше контекста для ошибки.

Вот простой пример:

from flask import jsonify

class InvalidUsage(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, payload=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.payload = payload

    def to_dict(self):
        rv = dict(self.payload or ())
        rv['message'] = self.message
        return rv

Теперь представление может поднять это исключение с сообщением об ошибке. Кроме того, дополнительная полезная нагрузка может быть предоставлена в виде словаря через параметр payload.

Регистрация обработчика ошибок

В этот момент представления могут поднять эту ошибку, но это немедленно приведет к внутренней ошибке сервера. Причина в том, что для этого класса ошибок не зарегистрирован обработчик. Однако его легко добавить:

@app.errorhandler(InvalidUsage)
def handle_invalid_usage(error):
    response = jsonify(error.to_dict())
    response.status_code = error.status_code
    return response

Использование в представлениях

Вот как представление может использовать эту функциональность:

@app.route('/foo')
def get_foo():
    raise InvalidUsage('This view is gone', status_code=410)