Рубрики
Без рубрики

Flask REST API -PART: 4- Исключение

Часть 4: Обработка исключений Привет! В предыдущей части серии мы узнали, как мы … Помечено с Python, Flask, API отдыха, ошибка обработки.

Flask Read API – ноль до yoda (7 части серии)

Часть 4: Обработка исключений

Привет! В предыдущем Часть серии, мы узнали, как мы можем добавить аутентификация и Авторизация Отказ В этой части мы собираемся узнать, как мы можем сделать нашу колбу более устойчивым к ошибкам, и как отправить правильное сообщение об ошибке клиенту.

Когда что-то идет не так в компьютерной программе, программа бросает определенное Исключение Давая подсказку пользователю, что пошло не так. В нашем приложении, когда что-то идет не так, например, пользователь E.G пытается создать другую учетную запись с уже использованным адресом электронной почты, они получают Ошибка внутреннего сервера И клиент не имеет представления о том, что они сделали неправильно. Итак, чтобы решить такие проблемы, мы собираемся использовать Обработка исключений Чтобы поймать такие исключения и отправить правильное сообщение об ошибке клиенту, указывающее, что пошло не так.

Мы собираемся использовать действительно полезную функцию Флэк-реститут который позволяет нам определить Пользовательские сообщения об ошибках Отказ

Давайте создадим новый файл ошибки внутри Ресурсы Папка и добавить следующий код:

cd resources
touch errors.py
#~/movie-bag/resources/errors.py

class InternalServerError(Exception):
    pass

class SchemaValidationError(Exception):
    pass

class MovieAlreadyExistsError(Exception):
    pass

class UpdatingMovieError(Exception):
    pass

class DeletingMovieError(Exception):
    pass

class MovieNotExistsError(Exception):
    pass

class EmailAlreadyExistsError(Exception):
    pass

class UnauthorizedError(Exception):
    pass

errors = {
    "InternalServerError": {
        "message": "Something went wrong",
        "status": 500
    },
     "SchemaValidationError": {
         "message": "Request is missing required fields",
         "status": 400
     },
     "MovieAlreadyExistsError": {
         "message": "Movie with given name already exists",
         "status": 400
     },
     "UpdatingMovieError": {
         "message": "Updating movie added by other is forbidden",
         "status": 403
     },
     "DeletingMovieError": {
         "message": "Deleting movie added by other is forbidden",
         "status": 403
     },
     "MovieNotExistsError": {
         "message": "Movie with given id doesn't exists",
         "status": 400
     },
     "EmailAlreadyExistsError": {
         "message": "User with given email address already exists",
         "status": 400
     },
     "UnauthorizedError": {
         "message": "Invalid username or password",
         "status": 401
     }
}

Как вы можете увидеть, сначала мы расширили Исключение Класс для создания различных пользовательских исключений, а затем мы создали Ошибки Словарь, который содержит сообщение об ошибке и коды состояния для каждого исключения. Теперь нам нужно добавить эти ошибки для Флэк-реститут API класс.

Обновление app.py Чтобы импортировать недавно созданные Ошибки Словарь и добавьте это как параметр на API класс.

#~/movie-bag/app.py

from database.db import initialize_db
 from flask_restful import Api
 from resources.routes import initialize_routes
+from resources.errors import errors

 app = Flask(__name__)
 app.config.from_envvar('ENV_FILE_LOCATION')

-api = Api(app)
+api = Api(app, errors=errors)
 bcrypt = Bcrypt(app)
 jwt = JWTManager(app)

Наконец, мы готовы выполнить некоторую обработку исключения в нашем приложении. Обновление Movie.py Просмотр функций в соответствии с следующим:

#~/movie-bag/resources/movie.py
 from flask import Response, request
 from database.models import Movie, User
 from flask_jwt_extended import jwt_required, get_jwt_identity
 from flask_restful import Resource
+
+from mongoengine.errors import FieldDoesNotExist, \
+NotUniqueError, DoesNotExist, ValidationError, InvalidQueryError
+
+from resources.errors import SchemaValidationError, +MovieAlreadyExistsError, \
+InternalServerError, UpdatingMovieError, DeletingMovieError, +MovieNotExistsError
+

 class MoviesApi(Resource):
     def get(self):
@@ -11,32 +15,57 @@ class MoviesApi(Resource):

     @jwt_required
     def post(self):
-        user_id = get_jwt_identity()
-        body = request.get_json()
-        user = User.objects.get(id=user_id)
-        movie =  Movie(**body, added_by=user)
-        movie.save()
-        user.update(push__movies=movie)
-        user.save()
-        id = movie.id
-        return {'id': str(id)}, 200
-        
+        try:
+            user_id = get_jwt_identity()
+            body = request.get_json()
+            user = User.objects.get(id=user_id)
+            movie =  Movie(**body, added_by=user)
+            movie.save()
+            user.update(push__movies=movie)
+            user.save()
+            id = movie.id
+            return {'id': str(id)}, 200
+        except (FieldDoesNotExist, ValidationError):
+            raise SchemaValidationError
+        except NotUniqueError:
+            raise MovieAlreadyExistsError
+        except Exception as e:
+            raise InternalServerError
+
+
 class MovieApi(Resource):
     @jwt_required
     def put(self, id):
-        user_id = get_jwt_identity()
-        movie = Movie.objects.get(id=id, added_by=user_id)
-        body = request.get_json()
-        Movie.objects.get(id=id).update(**body)
-        return '', 200
+        try:
+            user_id = get_jwt_identity()
+            movie = Movie.objects.get(id=id, added_by=user_id)
+            body = request.get_json()
+            Movie.objects.get(id=id).update(**body)
+            return '', 200
+        except InvalidQueryError:
+            raise SchemaValidationError
+        except DoesNotExist:
+            raise UpdatingMovieError
+        except Exception:
+            raise InternalServerError       

     @jwt_required
     def delete(self, id):
-        user_id = get_jwt_identity()
-        movie = Movie.objects.get(id=id, added_by=user_id)
-        movie.delete()
-        return '', 200
+        try:
+            user_id = get_jwt_identity()
+            movie = Movie.objects.get(id=id, added_by=user_id)
+            movie.delete()
+            return '', 200
+        except DoesNotExist:
+            raise DeletingMovieError
+        except Exception:
+            raise InternalServerError

     def get(self, id):
-        movies = Movie.objects.get(id=id).to_json()
-        return Response(movies, mimetype="application/json", status=200)
+        try:
+            movies = Movie.objects.get(id=id).to_json()
+            return Response(movies, mimetype="application/json", status=200)
+        except DoesNotExist:
+            raise MovieNotExistsError
+        except Exception:
+            raise InternalServerError


Давайте посмотрим на примере пост Метод HivoApi класс:

def post(self):
  try:
      user_id = get_jwt_identity()
      body = request.get_json()
      user = User.objects.get(id=user_id)
      movie =  Movie(**body, added_by=user)
      movie.save()
      user.update(push__movies=movie)
      user.save()
      id = movie.id
      return {'id': str(id)}, 200
  except (FieldDoesNotExist, ValidationError):
      raise SchemaValidationError
  except NotUniqueError:
      raise MovieAlreadyExistsError
  except Exception as e:
      raise InternalServerError

Здесь вы можете видеть, что мы завернули все возможности просмотра в попробуй ... кроме блокировать. Мы выполнили цепочку исключения, чтобы, когда мы получаем все исключение, мы бросаем исключение, которое мы определили в ошибки и Флэк-реститут Создает ответ на основе значений, которые мы определены в Ошибки толковый словарь.

Когда есть Поледосноtexist Исключение или ValidationError Исключение из Mongoengine Мы поднимаем SchemavalidationError Исключение, которое говорит клиенту, что их запрос JSON недействителен. Точно так же, когда пользователь пытается создать фильм с именем, который уже существует Mongoengine бросать NotuniqueError Исключение и поймать это исключение, которое мы поднимаем MoviealreadyexiSerror Что говорит пользователяму, что имя фильма уже существует.

И, наконец, если мы получим исключение, что мы не ожидали, тогда мы бросаем InternalservererRor Отказ

Давайте добавим подобное обращение с исключением на наше auth.py.

#~/movie-bag/resources/auth.py

 from database.models import User
 from flask_restful import Resource
 import datetime
+from mongoengine.errors import FieldDoesNotExist, NotUniqueError, DoesNotExist
+from resources.errors import SchemaValidationError, EmailAlreadyExistsError, UnauthorizedError, \
+InternalServerError

 class SignupApi(Resource):
     def post(self):
-        body = request.get_json()
-        user =  User(**body)
-        user.hash_password()
-        user.save()
-        id = user.id
-        return {'id': str(id)}, 200
+        try:
+            body = request.get_json()
+            user =  User(**body)
+            user.hash_password()
+            user.save()
+            id = user.id
+            return {'id': str(id)}, 200
+        except FieldDoesNotExist:
+            raise SchemaValidationError
+        except NotUniqueError:
+            raise EmailAlreadyExistsError
+        except Exception as e:
+            raise InternalServerError

 class LoginApi(Resource):
     def post(self):
-        body = request.get_json()
-        user = User.objects.get(email=body.get('email'))
-        authorized = user.check_password(body.get('password'))
-        if not authorized:
-            return {'error': 'Email or password invalid'}, 401
-        expires = datetime.timedelta(days=7)
-        access_token = create_access_token(identity=str(user.id), expires_delta=expires)
-        return {'token': access_token}, 200
+        try:
+            body = request.get_json()
+            user = User.objects.get(email=body.get('email'))
+            authorized = user.check_password(body.get('password'))
+            if not authorized:
+                raise UnauthorizedError
+ 
+            expires = datetime.timedelta(days=7)
+            access_token = create_access_token(identity=str(user.id), expires_delta=expires)
+            return {'token': access_token}, 200
+        except (UnauthorizedError, DoesNotExist):
+            raise UnauthorizedError
+        except Exception as e:
+            raise InternalServerError

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

Давайте попробуем создать пользователь с /API/AUTH/регистрация С любомым адресом электронной почты, скажем, testemail@gmail.com. . Теперь снова попробуйте создать другой пользователь с тем же адресом электронной почты. Мы получаем такой ответ:

{
    "message": "User with given email address already exists",
    "status": 400
}

Теперь пользователь нашего приложения может легко знать, что пошло не так.

Вы можете найти полный код этой части здесь

Что мы узнали из этой части серии?

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

До этого счастливого кодирования 😊

Flask Read API – ноль до yoda (7 части серии)

Оригинал: “https://dev.to/paurakhsharma/flask-rest-api-part-4-exception-handling-5c6a”