| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120 | import reimport sysfrom flask import current_app, got_request_exceptionfrom flask_restful import Api, http_status_messagefrom werkzeug.datastructures import Headersfrom werkzeug.exceptions import HTTPExceptionclass ExternalApi(Api):    def handle_error(self, e):        """Error handler for the API transforms a raised exception into a Flask        response, with the appropriate HTTP status code and body.        :param e: the raised Exception object        :type e: Exception        """        got_request_exception.send(current_app, exception=e)        headers = Headers()        if isinstance(e, HTTPException):            if e.response is not None:                resp = e.get_response()                return resp            status_code = e.code            default_data = {                'code': re.sub(r'(?<!^)(?=[A-Z])', '_', type(e).__name__).lower(),                'message': getattr(e, 'description', http_status_message(status_code)),                'status': status_code            }            if default_data['message'] and default_data['message'] == 'Failed to decode JSON object: Expecting value: line 1 column 1 (char 0)':                default_data['message'] = 'Invalid JSON payload received or JSON payload is empty.'            headers = e.get_response().headers        elif isinstance(e, ValueError):            status_code = 400            default_data = {                'code': 'invalid_param',                'message': str(e),                'status': status_code            }        else:            status_code = 500            default_data = {                'message': http_status_message(status_code),            }        # Werkzeug exceptions generate a content-length header which is added        # to the response in addition to the actual content-length header        # https://github.com/flask-restful/flask-restful/issues/534        remove_headers = ('Content-Length',)        for header in remove_headers:            headers.pop(header, None)        data = getattr(e, 'data', default_data)        error_cls_name = type(e).__name__        if error_cls_name in self.errors:            custom_data = self.errors.get(error_cls_name, {})            custom_data = custom_data.copy()            status_code = custom_data.get('status', 500)            if 'message' in custom_data:                custom_data['message'] = custom_data['message'].format(                    message=str(e.description if hasattr(e, 'description') else e)                )            data.update(custom_data)        # record the exception in the logs when we have a server error of status code: 500        if status_code and status_code >= 500:            exc_info = sys.exc_info()            if exc_info[1] is None:                exc_info = None            current_app.log_exception(exc_info)        if status_code == 406 and self.default_mediatype is None:            # if we are handling NotAcceptable (406), make sure that            # make_response uses a representation we support as the            # default mediatype (so that make_response doesn't throw            # another NotAcceptable error).            supported_mediatypes = list(self.representations.keys())  # only supported application/json            fallback_mediatype = supported_mediatypes[0] if supported_mediatypes else "text/plain"            data = {                'code': 'not_acceptable',                'message': data.get('message')            }            resp = self.make_response(                data,                status_code,                headers,                fallback_mediatype = fallback_mediatype            )        elif status_code == 400:            if isinstance(data.get('message'), dict):                param_key, param_value = list(data.get('message').items())[0]                data = {                    'code': 'invalid_param',                    'message': param_value,                    'params': param_key                }            else:                if 'code' not in data:                    data['code'] = 'unknown'            resp = self.make_response(data, status_code, headers)        else:            if 'code' not in data:                data['code'] = 'unknown'            resp = self.make_response(data, status_code, headers)        if status_code == 401:            resp = self.unauthorized(resp)        return resp
 |