Перехват исключений в Python Flask RestPlus

Я изо всех сил пытаюсь найти способ вернуть ответ, когда вызов одного из моих API не возвращает никакого результата. Я использую flask_restplus и безуспешно пытался использовать аннотацию @api.errorhandler, а затем я прибегнул к простому блоку try/except в своем классе.

В этом конкретном случае я пытаюсь вызвать конечную точку, где GUID является несуществующим пользователем в базе данных.

http://localhost:5000/api/users/b3d8e86b-f2ad-4b6a-b768-e7adc1d94ced

Конечная точка пользователя была определена следующим образом:

import logging

from flask import Response
from flask_restplus import Resource, fields
from neomodel.core import DoesNotExist

from app.api.restplus import api
from app.models import User

log = logging.getLogger(__name__)

ns = api.namespace('users', description='Users')

user = api.model('user', {
    'id': fields.Integer(readOnly=True, description='The internal id of of a user'),
    'user_id': fields.String(required=True, description='The user unique identifier')}
)

@ns.route('/')
@api.response(404, 'Users not found.')
class UsersList(Resource):

    @api.marshal_list_with(user)
    def get(self):
        """
        Returns a list of all users
        :return: list_of_users
        """
        try:
            users = User.nodes
            list_of_users = list(users)
            return list_of_users
    #        return json.dumps(dict(users = [user.serialize for user in list_of_users]))
        except DoesNotExist:
            return Response(('{"No Content": "No user nodes found"}'), status = 200, mimetype = 'application/json')


@ns.route('/<string:user_id>')
class UserItem(Resource):

   @api.marshal_with(user)
   def get(self, user_id):
        """
        Returns a user with the given user_id
        :param id: user_id
        :return: user
        """
        try:
            user = User.nodes.get(user_id=user_id)
            return user
        except User.DoesNotExist:
            return Response({'message': 'Could not locate a user with the user_id provided.'}, 404)

Моя инициализация выполняется в app/init.py:

import logging.config
from flask import Flask, Blueprint
from neomodel import clear_neo4j_database
from neomodel import db

from app.config import Configuration
from app.web_app.routes import webapp_mod
from app.data_loader.routes import dataloader_mod
from app.utilities import prepare_rerun
from app.api.endpoints.users import ns as users_namespace
from app.api.endpoints.sessions import ns as sessions_namespace
from app.api.endpoints.browsers import ns as browsers_namespace
from app.api.endpoints.os import ns as os_namespace
from app.api.endpoints.applications import ns as applications_namespace
from app.api.endpoints.tenants import ns as tenants_namepspace
from app.api.endpoints.devices import  ns as devices_namespace
from app.api.endpoints.environments import ns as environments_namespace

from app.api.restplus import api
from os import path


app = Flask(__name__)
app.config.from_object(Configuration)
app.register_blueprint(dataloader_mod, url_prefix='/data_loader')
log_file_path = path.join(path.dirname(path.abspath(__file__)), 'logging.conf')
logging.config.fileConfig(log_file_path)
log = logging.getLogger(__name__)

blueprint = Blueprint('api', __name__, url_prefix='/api')
api.init_app(blueprint)
api.add_namespace(users_namespace)
api.add_namespace(sessions_namespace)
api.add_namespace(browsers_namespace)
api.add_namespace(applications_namespace)
api.add_namespace(tenants_namepspace)
api.add_namespace(devices_namespace)
api.add_namespace(os_namespace)
api.add_namespace(environments_namespace)

Модуль resplus.py, в котором я определяю API на данный момент, имеет только определение объекта API, но я пытался следовать некоторым онлайн-примерам и определять в нем методы для обработки исключений с помощью аннотаций в объекте пользователей.

from flask_restplus import Api
from neomodel.core import DoesNotExist

api = Api(version='1.0', title='Users Activity Log',
          description='An API to retreive information about users'' activities in Infor Ming.le')

Однако, когда я делаю вызов, вместо получения ответа JSON с сообщением и кодом 404 я получаю:

{
    "id": null,
    "user_id": null
}

Заранее благодарю за любую помощь.


person Cracoras    schedule 08.01.2018    source источник
comment
Я испытываю ту же ошибку. Вы уже нашли ее решение?   -  person Molitoris    schedule 15.11.2019


Ответы (3)


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

Дело в том, что хотя у вас другой код возврата (и у вас ошибка), flask все равно пытается использовать @marshal_with(user). Сначала это может немного сбивать с толку.

Если вы посмотрите на это answer вы можете увидеть, как возвращать несколько типов ответов, не теряя функциональности swagger, плюс вы все еще получаете сортировку. Это немного более универсально, чем стандартный @marshal_with.

Существуют и другие способы достижения аналогичных результатов для ошибок: например. вы можете использовать функцию abort() (что в некоторых случаях может быть более логичным).

Другими словами, ваш преобразованный код будет выглядеть примерно так:

import logging

from flask import Response
from flask_restplus import Resource, fields, marshal
from neomodel.core import DoesNotExist

from app.api.restplus import api
from app.models import User

log = logging.getLogger(__name__)

ns = api.namespace('users', description='Users')

user = api.model('user', {
    'id': fields.Integer(readOnly=True, description='The internal id of of a user'),
    'user_id': fields.String(required=True, description='The user unique identifier')}
)

@ns.route('/')
@api.response(404, 'Users not found.')
class UsersList(Resource):

    @api.response(model=user, code=200)
    @api.response(404, 'Users not found.')
    def get(self):
        """
        Returns a list of all users
        :return: list_of_users
        """
        try:
            users = User.nodes
            list_of_users = list(users)
            return marshal(list_of_users)
        except DoesNotExist:
            return {"No Content": "No user nodes found"}, 404


@ns.route('/<string:user_id>')
class UserItem(Resource):

   @api.response(model=user, code=200)
   @api.response(code=404, 'Users not found.')
   def get(self, user_id):
        """
        Returns a user with the given user_id
        :param id: user_id
        :return: user
        """
        try:
            user = User.nodes.get(user_id=user_id)
            return marshal(user)
        except User.DoesNotExist:
            return {'message': 'Could not locate a user with the user_id provided.'}, 404

# More Endpoints - I hope this did the job...
person Cedric    schedule 01.10.2020

Ты можешь использовать

from flask_restplus import abort
abort(400, custom='value')

Чтобы прервать и через ошибки

or

namespace.abort(400, "An error occured") 
person Anthony Ugwu    schedule 07.07.2021

Вероятно, это уже не актуально, так как ваш вопрос возник довольно давно, но я столкнулся с этим, когда искал лучшие практики обработки исключений flask-restplus. Я не думаю, что ваша проблема связана с обработкой исключений flask-restplus. Похоже, ваш класс User не вызывает исключение DoesNotExist, когда пользователь не существует. Вы проверили, нажимаете ли вы блок Except:?

В любом случае, как только вы убедитесь, что действительно вызываете исключение DoesNotExist, вы можете зарегистрировать обработчик ошибок для этого исключения в своем init.py следующим образом:

@api.errorhandler(DoesNotExist)
def handle_user_does_not_exist_error(error):
    return ({'message': 'Could not locate a user with the user_id provided.'}, 404) 
person Fran Fabrizio    schedule 28.06.2018
comment
Эта проблема все еще сохраняется. - person Molitoris; 15.11.2019