使用JSON响应为Flask-RESTful定制参数验证 [英] Custom parameter validation with JSON response for Flask-RESTful

查看:1651
本文介绍了使用JSON响应为Flask-RESTful定制参数验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我使用Flask-RESTful来创建API端点,并使用这种方式指定URL:

  api.add_resource( ListVersionsByStaff'/ shots / versions /< int:project_id> /< int:staff_id>')
api.add_resource(ListSeasons,'/ seasons /< int:project_id>')

如果给定的参数不是 int ,它会返回一个HTML响应。



如何返回自定义的JSON错误响应,例如:

 除了InvalidParameter作为错误:
abort(err.status_code,** err.to_dict())

检查这个值的方式也行不通,参数总是键入String类型的项目(资源)

 
def get(self,project_id):
print(DEBUG:project_id is [,project_id,],file = sys.stderr)
print(DEBUG:Type is如果是isinstance(project_id,int):
pass
else:
message ='{,type(project_id),],file = sys.stderr)格式(project_id)
错误= {}
错误['resource'] =项目
}是不是一个有效的project_id提示:这是一个表示主键的数字。错误['field'] =project_id
错误['code'] =无效
错误['s tack_trace'] =
abort(400,message = message,errors = errors)

输出:

  DEBUG:project_id是[1] 
DEBUG:类型是[< class'str'> ; ]


解决方案

我的解决方案是扩展Flask-RESTful Api 类并实现我的自定义错误处理程序。 官方文档解释了一些关于扩展的内容,但是没有进入足够的细节。

自定义错误消息



我的应用程序中的所有错误都在这个结构中

$
$ b $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ None,
field = None,code = None,stack_trace = None):
Exception .__ init __(self)
self.message = message
如果status_code不是None:
self.status_code = status_code
self.resource = resource
self.field = field
self.code = code
self.stack_trace = stack_trace

def to_dict(self):
rv = {}
errors = {}
rv ['message'] = self.message
errors ['resource'] = self。资源
errors ['field'] = self.field
errors ['code'] = self.code
errors ['stack_trace'] = self.stack_trace
rv [错误] =错误
return rv

这是默认的Flask-RESTful返回值

 <!DOCTYPE HTML PUBLIC -  // W3C // DTD HTML 3.2 Final // EN> 
< title> 404未找到< / title>
< h1>未找到< / h1>
< p>在服务器上找不到请求的URL。如果您手动输入网址,请检查您的拼写,然后重试。< / p>

输出应该是JSON,格式为

  {
message:在服务器上找不到请求的URL,如果手动输入了URL,请检查拼写并重试。但是你的意思是/ projects /< int:project_id>或/ projects或/ project / new?,
errors:{
resource:null,
field:null,
code:invalid_url,
stack_trace:null
}
}



解决方案



扩展 Api 类并覆盖 404 错误
handle_error pre> class CustomApi(Api):
def handle_error(self,e):
code = getattr(e,'code',404)
if code == 404:
response_dict = Api.handle_error(self,e).__ dict__
resp_message = response_dict ['re ('。*)'。*','''','resf_message.decode'('utf-8'),flags = (错误信息,stack_trace = None,$ b $ resource = None,code =invalid_url,field = None)
返回self.make_response(err.to_dict() ,404)#{'message':something,'error':{'sub1':'val1'}},404)

return super(Api,self).handle_error(e)

handle_error 字典的格式为

  {'headers':Headers([('Content-Type','application / json'),('Content-Length ','263')]),'_status_code':404,'_status':'404 NOT FOUND','direct_passthrough':False,'_on_close':[],'response':[b'{\\\
消息:在服务器上找不到请求的URL。如果您手动输入网址,请检查拼写,然后重试。你已经请求了这个URI [/ projects / 1asd],但是你的意思是/ projects /< int:project_id>或/ projects或/ project / new?\\\
} \ n']}

I要重新使用已生成的'response':'message',但不是默认的格式。 message 是不正确的JSON格式,所以我去除了消息的内容,使用这个正则表达式

  error_message = re.sub(r'。*(。*)。*',r'\\1',resp_message.decode('utf-8'),flags =请注意, re.DOTALL  

>需要去除由Flask-RESTful添加的 \\\



构建JSON响应是 self.make_response(err.to_dict(),404)



404错误(例如400,500,503),错误只传递给原始的Flask-RESTful Api类。

请注意,当您创建Flask应用程序时,你需要使用你自定义的Api cla ss,并捕获所有的404错误:

$ p $ app = Flask(__ name__)
api = CustomApi(app,catch_all_404s =真)


I am using Flask-RESTful for creating API endpoints, and I specify the URL this way:

api.add_resource(ListVersionsByStaff, '/shots/versions/<int:project_id>/<int:staff_id>')
api.add_resource(ListSeasons, '/seasons/<int:project_id>')

While Flask-RESTful will return an error response if the given argument is not int, it will return a HTML response.

How can I return a custom JSON error response, for example:

except InvalidParameter as err:
            abort(err.status_code, **err.to_dict())

Checking for the value this way also does not work, the parameter is always type String

class SpecificProject(Resource):
    def get(self, project_id):
        print("DEBUG: project_id is [", project_id, "]", file=sys.stderr)
        print("DEBUG: Type is  [", type(project_id), "]", file=sys.stderr)
        if isinstance(project_id, int):
            pass
        else:
            message = "'{}' is not a valid project_id. Hint: this is a number representing primary key.".format(project_id)
            errors = {}
            errors['resource'] = "Project"
            errors['field'] = "project_id"
            errors['code'] = "invalid"
            errors['stack_trace'] = ""
            abort(400, message=message, errors=errors)

output:

DEBUG: project_id is [ 1 ]
DEBUG: Type is  [ <class 'str'> ]

解决方案

My solution is to extend the Flask-RESTful Api class and implement my custom error handler. The official documentation explains a little bit about extending, but did not go into sufficient details.

Custom Error Message

All errors in my application is in this structure

class InvalidParameter(Exception):
    status_code = 400

    def __init__(self, message, status_code=None, resource=None,
                field=None, code=None, stack_trace=None):
        Exception.__init__(self)
        self.message = message
        if status_code is not None:
            self.status_code = status_code
        self.resource = resource
        self.field = field
        self.code = code
        self.stack_trace = stack_trace

    def to_dict(self):
        rv = {}
        errors = {}
        rv['message'] = self.message
        errors['resource'] = self.resource
        errors['field'] = self.field
        errors['code'] = self.code
        errors['stack_trace'] = self.stack_trace
        rv['errors'] = errors
        return rv

This is what Flask-RESTful return by default

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<title>404 Not Found</title>
<h1>Not Found</h1>
<p>The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again.</p>

The output should be JSON, in this format

{
    "message": "The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again. You have requested this URI [/projects/1asd] but did you mean /projects/<int:project_id> or /projects or /project/new ?",
    "errors": {
        "resource": null,
        "field": null,
        "code": "invalid_url",
        "stack_trace": null
    }
}

Solution

Extend the Api class and overwrite the handle_error method for 404 errors

class CustomApi(Api):
    def handle_error(self, e):
        code = getattr(e, 'code', 404)
        if code == 404:
            response_dict = Api.handle_error(self, e).__dict__
            resp_message = response_dict['response'][0]
            error_message = re.sub(r'.*"(.*)".*', r'\1', resp_message.decode('utf-8'), flags=re.DOTALL)
            err = InvalidParameter(error_message, stack_trace=None,
                                    resource=None, code="invalid_url", field=None)
            return self.make_response(err.to_dict(), 404) #{'message': "something", 'error': {'sub1': 'val1'}}, 404)

        return super(Api, self).handle_error(e)

The handle_error dictionary is in this format

{'headers': Headers([('Content-Type', 'application/json'), ('Content-Length', '263')]), '_status_code': 404, '_status': '404 NOT FOUND', 'direct_passthrough': False, '_on_close': [], 'response': [b'{\n    "message": "The requested URL was not found on the server.  If you entered the URL manually please check your spelling and try again. You have requested this URI [/projects/1asd] but did you mean /projects/<int:project_id> or /projects or /project/new ?"\n}\n']}

I want to re-use the 'response':'message' that was generated, but not in the default format. message was not not properly JSON formatted, so I strip out everything except for the content of message, using this regex

error_message = re.sub(r'.*"(.*)".*', r'\1', resp_message.decode('utf-8'), flags=re.DOTALL)

Note that re.DOTALL is needed to strip out the \n that was added by Flask-RESTful.

The code that actually builds the JSON response is self.make_response(err.to_dict(), 404)

For all other non-404 errors (e.g. 400, 500, 503), the error is just passed along to the original Flask-RESTful Api class.

Note that when you create your Flask application, you need to use your custom Api class, and catch all 404 errors:

app = Flask(__name__) 
api = CustomApi(app, catch_all_404s=True)

这篇关于使用JSON响应为Flask-RESTful定制参数验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆