flask的Python错误处理期望json模块抛出内部服务器错误



我正在使用flask_expects_json库来验证API请求中的json模式。

然而,这是有效的,当请求无效时,会在日志中抛出错误,但API不会返回错误消息的详细信息。该响应只是一个通用消息";内部服务器错误";。

错误消息:

{"event": "Exception on /ingest [PUT]", "logger": "app", "level": "error", "exception": "Traceback (most recent call last):n  File "/usr/local/lib/python3.8/site-packages/flask_expects_json/__init__.py", line 30, in decorated_functionn    validate(data, schema)n  File "/usr/local/lib/python3.8/site-packages/jsonschema/validators.py", line 934, in validaten    raise errornjsonschema.exceptions.ValidationError: 'location' is a required propertynnFailed validating 'required' in schema}

响应:

{
"message": "Internal Server Error"
}

问题:如何处理flask_expects_json中的错误,并返回错误的详细信息,而不是服务器错误?换句话说,我需要响应中错误的详细信息,例如:

{
"error": "'location' is a required property"
}

这是我目前掌握的代码。

from flask import Flask, request, jsonify, current_app
from flask_restx import Resource, Api
from flask_expects_json import expects_json
def create_app():
app = Flask(__name__)
api = Api(app, version='1.0', title='Test API', description='Test API Description')
@api.route('/ingest', endpoint="ingest", methods=['GET', 'POST', 'PUT', 'DELETE'])
class Ingest(Resource):
@expects_json(test_schema)
def get(self, *args, **kwargs):
# logic removed for brevity
result = get_result()
return result
return app
if __name__ == '__main__':
app.run()

我还尝试根据关于错误处理和其他类似堆栈溢出问题的flask文档编写一个错误处理程序来捕捉这些错误,但它似乎没有起到任何作用。

class BadRequest(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
@app.errorhandler(BadRequest)
def handle_bad_request(error):
response = jsonify(error.to_dict())
response.status_code = error.status_code
return response

更新:此错误可能是由gunicorn structlog配置导致的,该配置试图将ValidationError转换为JSON,但失败,因此引发500错误。

这是gunicorn.conf.py

import structlog
import os
from datetime import datetime
if os.environ.get('ENV') == 'development':
reload = True
hostname = os.uname().nodename
timestamp = datetime.today().strftime('%Y-%m-%d')
pre_chain = [
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.stdlib.PositionalArgumentsFormatter(),
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.UnicodeDecoder(),
structlog.processors.TimeStamper(fmt='iso', utc=True),
]
logconfig_dict = {
"version": 1,
"disable_existing_loggers": True,
"formatters": {
"json_formatter": {
"()": structlog.stdlib.ProcessorFormatter,
"processor": structlog.processors.JSONRenderer(),
"foreign_pre_chain": pre_chain,
}
},
"handlers": {
"error_console": {
"class": "logging.FileHandler",
"formatter": "json_formatter",
"filename": "/home/logs/error_console_{}_{}.log".format(hostname, timestamp),
"mode": "a"
},
"console": {
"class": "logging.FileHandler",
"formatter": "json_formatter",
"filename": "/home/logs/console_{}_{}.log".format(hostname, timestamp),
"mode": "a"
}
},
"loggers": {
'gunicorn.error': {
'handlers': ['console'],
'level': os.environ.get('APP_LOG_LEVEL', 'INFO'),
'propagate': False,
},
'gunicorn.access': {
'handlers': ['console'],
'level': os.environ.get('APP_LOG_LEVEL', 'INFO'),
'propagate': False,
}
}
}

这是当它试图抛出400 Bad Request错误,但随后遇到TypeError时发生的500错误。CCD_ 1。

Failed validating 'required' in schema

During handling of the above exception, another exception occurred:
Traceback (most recent call last):

File "/usr/local/lib/python3.8/json/__init__.py", line 231, in dumps
return _default_encoder.encode(obj)
File "/usr/local/lib/python3.8/json/encoder.py", line 199, in encode
chunks = self.iterencode(o, _one_shot=True)
File "/usr/local/lib/python3.8/json/encoder.py", line 257, in iterencode
return _iterencode(o, 0)
File "/usr/local/lib/python3.8/json/encoder.py", line 179, in default
raise TypeError(f'Object of type {o.__class__.__name__} '
TypeError: Object of type ValidationError is not JSON serializable

更新2[WORKAROUND]:如前所述,我认为这个问题是由于expects_json抛出ValidationError时的错误处理问题引起的。它可能与gunicorn structlog配置有关,也可能是可以在expects_json库中修复的内容。作为一种解决方法,我删除了expects_json,并创建了一个自定义装饰器来使用jsonschema库验证json模式,类似于这个相关问题中接受的答案。Flask:Decorator验证JSON和JSON模式

这是我的解决方案的完整代码。同样,这实际上并不能直接解决问题,但却是一种可以接受的解决方法,并且确实提供了更多的控制。

from flask import request, jsonify, make_response, current_app
import os, json, jsonschema
def validate_json_schema(schema_name):
def decorator(f):
def wrapper(*args, **kwargs):
basedir = current_app.config['BASE_DIR']
jsonmodelsdir = basedir + '/jsonmodels'
if not request.json:
error_msg = "Missing JSON data."
return make_response(jsonify({'error': 'invalidRequest', 'message': error_msg}), 400)
try:
with open('{}/{}.json'.format(jsonmodelsdir, schema_name)) as json_file:
json_model = json.load(json_file)
except Exception as e:
error_msg = "Server error: Unable to get json model."
current_app.logger.error(error_msg)
current_app.logger.error(e)
return make_response(jsonify({'error': 'unavailable', 'message': error_msg}), 500)
try:
jsonschema.validate(request.json, json_model)
except json.decoder.JSONDecodeError as e:
error_msg = "Invalid JSON format: {}".format(e)
return make_response(jsonify({'error': 'invalidRequest', 'message': error_msg}), 400)
except jsonschema.exceptions.ValidationError as e:
error_msg = "Invalid JSON schema: {}".format(e)
return make_response(jsonify({'error': 'invalidRequest', 'message': error_msg}), 400)
except Exception as e:
error_msg = "Server error: Unable to validate json model."
current_app.logger.error(e)
return make_response(jsonify({'error': 'unavailable', 'message': error_msg}), 500)
return f(*args, **kwargs)
return wrapper
return decorator

您是否尝试在调试模式下运行flask?

if __name__ == '__main__':
app.run(debug=True)

文档中有一个flask_expects_json错误处理示例:https://pypi.org/project/flask-expects-json/

我在这里的回答扩展了该文件,以进一步澄清如何做到这一点,

进口

from flask import make_response, jsonify
from flask_expects_json import expects_json
from jsonschema import ValidationError

示例模式

schema = {
'type': 'object',
'properties': {
'data': {'type': 'string', "minLength": 31}
},
'required': ['data']
}

示例错误处理逻辑

@app.errorhandler(400)
def bad_request(error):
if isinstance(error.description, ValidationError):
original_error = error.description
return make_response(jsonify({'error': original_error.message}), 400)
# handle other "Bad Request"-errors
return error

端点后示例

@app.post("/temp")
@expects_json(schema)
def temp_post():
if request.is_json:
request_json = request.get_json()
data = request_json.get("data")
return make_response(jsonify({"data_sent": data}), 200)

当用而不是的JSON对象调用该端点时;数据";以下错误消息将返回

{
"error": "'data' is a required property"
}

(也许在最初提出这个问题时,这些例子并不存在(

相关内容

最新更新