Python Flask中的自定义身份验证和参数检查
我目前正在重构两年前写的一个Flask应用程序,我怀疑我做了一些事情没有使用该库可能做到的优雅和干净。因此,我想就如何改善现状征求一些建议:Python Flask中的自定义身份验证和参数检查,python,http,flask,decorator,python-2.x,Python,Http,Flask,Decorator,Python 2.x,我目前正在重构两年前写的一个Flask应用程序,我怀疑我做了一些事情没有使用该库可能做到的优雅和干净。因此,我想就如何改善现状征求一些建议: 该应用程序提供了许多API端点,每个端点都可以通过/形式的路由访问,其中是10个不同类别中的一个 对于每个,我创建了一个不同的Python模块.py,将其放置在api/子目录和一个不同的flask.Blueprint(实际上是它的一个子类,见下文)中,并将其注册到主app=flask(\uu name\uuu) 每个模块.py都包含许多函数,这些函数用作各
/
形式的路由访问,其中
是10个不同类别中的一个
,我创建了一个不同的Python模块.py
,将其放置在api/
子目录和一个不同的flask.Blueprint
(实际上是它的一个子类,见下文)中,并将其注册到主app=flask(\uu name\uuu)
.py
都包含许多函数,这些函数用作各自类别的端点,并且每个函数都用路由装饰器装饰参数
字段的一部分传递。因此,在调用相应的端点函数之前,将检查是否提供了具有正确名称的正确数量的参数
此外,应用程序需要检查是否允许客户端调用某个函数。为此,将读取由Web服务器设置的环境变量SSL\u CLIENT\u CERT
(在我的例子中,lighttpd over FCGI)并将其指纹与某些内部权限文件进行比较
因为我不太知道把逻辑放在哪里去做上面的事情,所以我对flask.Blueprint进行了子类化,并编写了我自己的(修改的)route
decorator(custom\u route
)。此装饰器现在要么返回定制的错误响应(flask.response
object),要么返回定制的成功响应(从而使用从客户端传递的参数调用端点函数)
因此,一个模块category_xy.py
如下所示:
category_xy = CustomBlueprint('category_xy', __name__)
@category_xy.custom_route('/category_xy/some_endpoint',
auth_required=True,
methods=['GET', 'POST'])
def some_endpoint(foo, bar):
# do stuff
return '123'
将CustomBlueprint
在单独的文件中定义为(部分伪代码):
这是可行的,但感觉一点也不干净,我认为应该有更好、更干净的方法来做到这一点。将所有这些逻辑放在自定义装饰器中感觉非常错误
有经验的人能提供一些想法和/或最佳实践吗?首先,您可以切换到-API,它允许您将视图函数作为类编写:
from flask import Blueprint, Response, render_template
from flask.views import View
from models import db, CategoryXY # Let`s have here a database model
category_blueprint = Blueprint('category_xy', __name__)
class CategoryXYView(View):
def get_list(self):
items = db.session.query(CategoryXY).all()
return render_template('category_xy_list.html', items=items)
def get_one(self, item_id):
item = db.session.query(CategoryXY).first()
if item is None:
return Response(status=404)
return render_template('category_xy_edit.html', item=item)
def dispatch_request(self, item_id):
"""Required method which handles incoming requests"""
if item_id is None:
return self.get_list()
return self.get_one(item_id)
category_xy_view = CategoryXYView.as_view('category_xy')
category_blueprint.add_url_rule(
'category_xy',
view_func=category_xy_view,
defaults={'item_id': None}
)
category_blueprint.add_url_rule(
'category_xy/<item_id>',
view_func=category_xy_view
)
为了自动生成路由,您可以为BaseView
编写一个元类。每个新类的路由将在类定义后生成:
class ViewMeta(type):
def __new__(mcs, name, bases, attributes):
cls = type(name, bases, attributes)
if cls.route is not None:
view_function = cls.as_view(cls.route)
category_blueprint.add_url_rule(
cls.route,
view_func=view_function,
defaults={'item_id': None}
)
category_blueprint.add_url_rule(
'{}/<item_id>'.format(cls.route),
view_func=view_function
)
return cls
class BaseView(View):
__metaclass__ = ViewMeta
route = None
model = None
# Other common attributes
class CategoryXYView(BaseView):
route = 'category_xy'
model = CategoryXY
class ViewMeta(类型):
定义新(mcs、名称、基础、属性):
cls=类型(名称、基础、属性)
如果cls.route不是None:
视图功能=cls.as\U视图(cls.route)
类别\u蓝图。添加\u url\u规则(
中环线,
视图函数=视图函数,
默认值={'item_id':None}
)
类别\u蓝图。添加\u url\u规则(
“{}/”格式(cls.route),
查看功能=查看功能
)
返回cls
类BaseView(视图):
__元类\视图元数据
路线=无
型号=无
#其他共同属性
类类别yxyView(基本视图):
路线='category_xy'
模型=类别
我想您的类别有一些共同点:类似的端点名称、授权机制、验证函数。是吗?是的!每个端点函数都会触发服务器上的某些操作。一个类别中的所有操作都有一定的相关性,并且通常也有类似的端点名称。
class BaseView(View):
model = None
edit_template = None
list_template = None
def get_one(self, item_id):
item = db.session.query(self.model).filter_by(id=item_id).first()
if item is None:
return Response(status=404)
return render_template(self.edit_template, item=item)
def get_list(self):
items = db.session.query(self.model).all()
return render_template(self.list_template, items=items)
def dispatch_request(self, item_id):
if item_id is None:
return self.get_list()
return self.get_one(item_id)
class CategoryXYView(BaseView):
model = CategoryXY
edit_template = 'category_xy_edit.html'
list_template = 'category_xy_list.html'
category_xy_view = CategoryXYView.as_view('category_xy')
category_blueprint.add_url_rule(
'category_xy',
view_func=category_xy_view,
defaults={'item_id': None}
)
category_blueprint.add_url_rule(
'category_xy/<item_id>',
view_func=category_xy_view
)
class CategoryXXView(BaseView):
model = CategoryXX
edit_template = 'category_xx_edit.html'
list_template = 'category_xx_list.html'
def get_one(self, item_id):
item = db.session.query(self.model).first()
if item is None:
return Response(status=404)
xy_items = db.session.query(CategoryXY).all()
return render_template(self.edit_template, item=item, xy_items=xy_items)
class ViewMeta(type):
def __new__(mcs, name, bases, attributes):
cls = type(name, bases, attributes)
if cls.route is not None:
view_function = cls.as_view(cls.route)
category_blueprint.add_url_rule(
cls.route,
view_func=view_function,
defaults={'item_id': None}
)
category_blueprint.add_url_rule(
'{}/<item_id>'.format(cls.route),
view_func=view_function
)
return cls
class BaseView(View):
__metaclass__ = ViewMeta
route = None
model = None
# Other common attributes
class CategoryXYView(BaseView):
route = 'category_xy'
model = CategoryXY