前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >由表单验证说起,关于在C#中尝试链式编程的实践

由表单验证说起,关于在C#中尝试链式编程的实践

作者头像
HOHO
发布2020-04-27 10:14:34
1.2K0
发布2020-04-27 10:14:34
举报
文章被收录于专栏:用户5447549的专栏

在web开发中必不可少的会遇到表单验证的问题,为避免数据在写入到数据库时出现异常,一般比较安全的做法是前端会先做一次验证,通过后把数据提交到后端再验证一次,因为仅仅靠前端验证是不安全的,有太多的http请求工具可以轻松绕过你的前端验证把危险数据提交到后端,所以,之前不做后端参数验证的同学赶快检查一下你的代码~别中招了

那么,故事就是有关于后端验证。

这里举一个项目中真实的注册场景,账号注册主要包含2个信息:手机号和验证码,因为我这里是用webapi的post方式从前端拿数据,所以封装成了一个MemberRegister对象。以最基础的非空验证为例,通常要写如下代码:

如果还要加上手机号格式验证,还得再来一个if。一旦要验证的信息多的话代码行就会很多,看着很冗余。想着既然做的都是同一件事,那能不能封装一下减少代码行?架构师allen说可以试一下链式编程,也就是类似Jquery的xxxx.attr().css().html().show()这样,看起来还不错的样子,那就干吧。

其实C#里也有类似的用法,比如Linq里面的xxxx.Where().OrderBy().Select()这种,但是这种实际上每次返回的都是不同的对象,然后执行对象里的方法,这并不适合我的需求,因为我执行的验证方法肯定都是同一个,比如validate().validate().validate()这种,于是决定用扩展方法来实现。先定义一个被扩展的对象:

代码语言:javascript
复制
public class ValidateResult<T>
    {
        public List<string> Errors { get; set; }
        public T Entity { get; private set; }
 
        public ValidateResult(T entity)
        {
            Errors = new List<string>();
            Entity = entity;
        }
}

定义扩展方法:

代码语言:javascript
复制
public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage)
        {
            if (!predicate(target.Entity))
            {
                target.Errors.Add(errorMessage) ;
            }
            return target;
        }

使用办法:

代码语言:javascript
复制
var error = new ValidateResult<MemberRegister>(model)
                .Validate(m => m != null, ResponseTip.ParamError)
                .Validate(m => !string.IsNullOrEmpty(m.Phone), ResponseTip.PhoneRequired)
                .Validate(m => !string.IsNullOrEmpty(m.CodeValue), ResponseTip.ValidateCodeRequired)
                .Errors;

理想中的情况是,可以判断error里面有没有错误信息,如果有的话就返回错误信息,没有就做后面的操作。但实际上碰到一个问题,当model为null的时候,第一步验证没有问题,但第二步的时候就报错了,未将对象引用到实例,原因是model已经是null了再取model.Phone不出错才怪。问题找到了,那就想着如果model为null就不执行后面的验证了,想法不错但想了很久就是没找到办法实现。不知所措的时候,断点跟了一下出错的代码,发现报错的地方是在执行if (!predicate(target.Entity))的时候,于是换了一个思路,改进一下代码:

代码语言:javascript
复制
    public class ValidateResult<T>
    {
        public string Error { get; set; }
        public T Entity { get; private set; }
 
        public ValidateResult(T entity)
        {
            Entity = entity;
        }
    }

扩展方法:

代码语言:javascript
复制
        public static ValidateResult<T> Validate<T>(this ValidateResult<T> target, Predicate<T> predicate, string errorMessage)
        {
            if (string.IsNullOrEmpty(target.Error))
            {
                if (!predicate(target.Entity))
                {
                    target.Error = errorMessage;
                }
            }
            return target;
        }

改进后的代码把ValidateResult里的Errors取消了换成了string类型的Error(要那么多错误提示也没什么用,一个就够了),然后验证失败后就更新这个属性,验证的时候如果这个属性string.IsNullOrEmpty(target.Error)就表示前面的验证都通过了本次可以继续验证,如果! string.IsNullOrEmpty(target.Error)就表示前面的验证已经失败了本次不用验证,要验证的对象原封不动的返回。这样子就不会报错了,然后调用结果判断Error是否NullOrEmpty再做相应操作。测试一下,没有问题。代码演变为:

优点

可读性个人觉得并不比直接if差,分行显示的话还是能很清晰看出具体的验证项。

省去了每次判断的if语句和return,支持自定义验证规则和错误提示。

减少了代码的行数。

缺点

某次验证失败不能中断后面的验证,多执行了不必要的代码,这点用if可以避免。

总结

完了以后去网上找了一些C#链式编程的问题,有支持的也有反对的,反对的人说代码可读性不太好、简单的问题复杂化等等。经过实际实践,我觉得这个问题偏向于个人喜好,谈不上好坏,怎样用着爽、开发效率高就行。不喜欢的还请轻点拍砖。

当然,关于这个问题有更好解决方案的希望能交流一下。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2016-08-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
验证码
腾讯云新一代行为验证码(Captcha),基于十道安全栅栏, 为网页、App、小程序开发者打造立体、全面的人机验证。最大程度保护注册登录、活动秒杀、点赞发帖、数据保护等各大场景下业务安全的同时,提供更精细化的用户体验。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档