用户输入验证重构方法比较——以Django示例说明

我们在一个Web应用程序中,需要对来自于用户输入或数据库查询的数据进行验证,然后在物理资源上执行某些操作。其设计可用下图简单表示:

用户输入 <=> 模型对象 <=> 数据库存储

当数据来源于用户输入时,需要进行验证;但当数据来源于数据库查询结果时,则不必验证,因为如果数据库中存在该记录,那么这些被验证字段的值一定已经存在。

我们试图重构代码,以便将验证操作放在对象的构造函数中进行,而不是使用传统的单独的验证例程。

我们想知道哪种方法更好?(方法1(传统)和方法2的基本区别在于,方法1中的验证不是强制的,并且与实例化对象无关;而方法2则将验证与实例化对象绑定在一起,并强制要求所有请求进行验证。)

下面是设计1和设计2的两个代码示例:

方法1:

# 处理单个请求。
# 步骤:1. 验证所有传入的数据。2. 实例化对象。
ValidateAttributes(request) # raise Exceptions if failed
resource = Resource(**request)

方法2:

# 将其提取出来,因为它与对象无关。
# raise Exceptions if some required params missing.
# steps: 1. Check whether its a batching request. 2. instantiate the object.
#           (validations are performed inside the constructor)
CheckIfBatchRequest(request) 
resource = Resource(**request) # raise Exceptions when validations failed

对于批处理请求:

方法1:

# 步骤:1. 验证每个请求,如果发现任何错误,则返回错误给客户端。
#        2. 执行对象实例化和创建过程。异常会被捕获。
#        3. 完成所有操作后,通过电子邮件发送任何错误。
for request in batch_requests:
    try:    
        ValidateAttribute(request)
    except SomeException, e:
        return ErrorPage(e)
errors = []
for request in batch_requests:
    try:
        CreatResource(Resource(**request), request)
    except CreationError, e:
        errors.append('failed to create with error: %s', e)
email(errors)

方法2:

# 步骤:1. 验证请求中的批处理作业相关数据。
#        2. 如果成功,则为每个请求创建对象并进行验证。
#        3. 如果出现异常,则返回发现的错误,否则,
#           返回包含(对象,请求)对的列表
#        4. 执行创建过程并通过电子邮件发送如果遇到的任何错误。
CheckIfBatchRequest(request)
request_objects = []
for request in batch_requests:
    try:
        resource = Resource(**request)
    except SomeException, e:
        return ErrorPage(e)
    request_objects.append((resource, request))
email(CreateResource(request_objects)) # the CreateResource will also need to be refactored.

方法1的优点和缺点:

更接近业务逻辑。当对象来自数据库查询时,不进行冗余验证检查。验证例程更容易维护和阅读。
方法2的优点和缺点:

对调用者来说简单干净。即使来自数据库查询,也必须进行验证。验证的可维护性和可读性较差。

2、解决方案

在Django中,在构造函数中进行验证并不是一种“Django式”的方法。由于要验证的数据来自客户端,因此使用新表单(可能采用ModelForm)是验证的最佳方法,因为它将所有关注点都包装到一个API中:它提供了明智的验证默认值(并且可以轻松自定义),此外,模型表单将数据输入端(html表单)与数据提交端(model.save())集成在一起。

不过,听起来你的项目可能是混乱的遗留项目,最初可能没有时间来重写所有表单处理程序以使用新表单。因此,以下是一些想法:

首先,将一些验证放在模型本身中并不是“非Django式”的——毕竟,html表单提交可能不是新数据的唯一来源。你可以覆盖save()方法或使用信号来在保存时清理数据或在数据无效时抛出异常。从长远来看,Django将具备模型验证功能,但目前尚未实现; 在此期间,你应该将此视为一种“安全保障”,以确保你不会向数据库中提交无效数据。换句话说,在提交之前,你仍然需要逐字段验证,以便知道在无效输入时向用户显示什么错误。

我建议你这样做。为每个需要验证的项目创建一个新的表单类,即使你最初没有使用它们。 Malcolm Tredinnick概述了一种使用表单系统中提供的钩子进行模型验证的技术。阅读一下(这非常简单而优雅),然后将其引入你的模型中。一旦定义并使用了新的表单类,你就会发现(如果将现有表单模板和相应的验证剔除,并使用表单框架处理表单POST)这并不难——实际上会极大地简化你的代码。有一个小小的学习曲线,但表单API非常周全,你会感谢它让你的代码变得多么简洁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值