| import json |
| |
| from aiohttp import web |
| from json import JSONDecodeError |
| |
| from aiohttp.web_exceptions import HTTPUnprocessableEntity |
| from marshmallow.exceptions import ValidationError |
| |
| from app.api.v2 import errors |
| from app.api.v2.schemas.error_schemas import JsonHttpErrorSchema |
| |
| |
| class JsonHttpErrorResponse: |
| """Base class for json formatted versions of aiohttp responses.""" |
| |
| def __init__(self, error, details=None, **kwargs): |
| kwargs['content_type'] = 'application/json' |
| kwargs['text'] = JsonHttpErrorSchema.serialize(error, details) |
| super().__init__(**kwargs) |
| |
| |
| class JsonHttpBadRequest(JsonHttpErrorResponse, web.HTTPBadRequest): |
| """An HTTP 400 response with a json formatted body.""" |
| |
| |
| class JsonHttpForbidden(JsonHttpErrorResponse, web.HTTPForbidden): |
| """An HTTP 403 response with a json formatted body.""" |
| pass |
| |
| |
| class JsonHttpNotFound(JsonHttpErrorResponse, web.HTTPNotFound): |
| """An HTTP 404 response with a json formatted body.""" |
| |
| |
| @web.middleware |
| async def apispec_request_validation_middleware(request, handler): |
| """Middleware to handle errors thrown by schema validation |
| |
| Must be added before `validation_middleware`""" |
| try: |
| return await handler(request) |
| except TypeError as ex: |
| # ex: Schema `post_load()` instantiates an object, but required |
| # argument is missing |
| raise JsonHttpBadRequest( |
| error='Error parsing JSON', |
| details=str(ex) |
| ) |
| except AttributeError as ex: |
| # ex: JSON contains attribute that does not exist in Schema |
| # Or any other AttributeError... |
| raise JsonHttpBadRequest( |
| error='AttributeError', |
| details=str(ex) |
| ) |
| except ValidationError as ex: |
| # ex: List of objects sent when single object expected |
| formatted_message = json.dumps({"json": ex.messages}, indent=2) |
| raise HTTPUnprocessableEntity( |
| text=formatted_message |
| ) |
| except JSONDecodeError as ex: |
| raise JsonHttpBadRequest( |
| error='Unexpected error occurred while parsing json', |
| details=str(ex) |
| ) |
| |
| |
| @web.middleware |
| async def json_request_validation_middleware(request, handler): |
| """Middleware that converts json decoding and marshmallow validation |
| errors into 400 responses w/ json bodies. |
| """ |
| try: |
| return await handler(request) |
| except errors.DataValidationError as ex: |
| raise JsonHttpBadRequest( |
| error=str(ex), |
| details={ex.name: ex.value} |
| ) |
| except errors.RequestValidationError as ex: |
| raise JsonHttpBadRequest('Received invalid json', details=ex.errors) |
| except errors.RequestUnparsableJsonError: |
| raise JsonHttpBadRequest('Unexpected error occurred while parsing json') |