0

0

ASP.NET全栈开发之在MVC中使用服务端验证(二)

无忌哥哥

无忌哥哥

发布时间:2018-07-20 09:20:12

|

2006人浏览过

|

来源于php中文网

原创

 首先声明,这篇博文是完善.asp.net全栈开发之在mvc中使用服务端验证 的,所以重复内容,我就不过多的阐述,很多问题都是在实践中去发现,然后再去完善,这篇博文也一样,建立在已阅 “.asp.net全栈开发之在mvc中使用服务端验证” 的基础上。

在上一篇中,虽然我们完成了服务端验证,但我们还是需要在Action里调用验证器来进行验证,像这样。

   [HttpPost]        
       public ActionResult ValidatorTest(Person model)
        {var result = this.ValidatorHub.PersonValidator.Validate(model);            
            if (result.IsValid)
            { 
                return Redirect("https://www.baidu.com");
            }else
            { 
                this.ValidatorErrorHandler(result);
            }            
            return View();
        }

很可恶,如果我需要验证,我需要在每一个Action 里像这样写,一次实验也就罢了,如果真要在每个Action里像这样干,我想到时候你一定会很讨厌这些代码的。至少我是这样认为。所以我很讨厌我之前的写法。

现在我想干嘛呢?我们知道其实MVC内置了一个数据校验。这里不过多介绍它,(偶尔适当的照照轮子,也有许多好处的)。这里简单描述下它的用法。

  [HttpPost]        
  public ActionResult ValidatorTest(Person model)
        {
            if (ModelState.IsValid)
            { /// ok }            
            return View();
        }

和咱们之前那样写比起来是精简了许多,但我还是觉得吧,他还是要在每个Action 里调用ModelState.IsValid,虽然只有一个if,但这不是我想要的,我希望它能像这样

  [HttpPost]        
      public ActionResult ValidatorTest(Person model)
        {            
        // 
            //  一大堆代码            
            //            
                return Redirect("https://www.baidu.com");
        }

不要影响我正常的编程,而我也不去做哪些重复的事。

换句话说,其实就是在执行我Action之前就去把数据给校验了。

于是我们想到了MVC给我们提供的Filter,OnActionExecuting,打开我们的ControllerEx,在里面重写OnActionExecuting,他有一个参数ActionExecutingContext,通过名字我们大致了解了,这个参数是个Action相关的上下文,那他一定装了Action相关的数据

我就不墨迹了,先直接上代码,其实这些代码也只是我刚刚才写出来的而已,我对这个参数也不是很了解,通过一个一个去尝试,慢慢得就试出来了。

 protected override void OnActionExecuting(ActionExecutingContext filterContext)
        {            
            var existError = false;            
            foreach (var value in filterContext.ActionParameters.Values)
            {                
                var modelValidatorPropertyInfo = this.ValidatorHub.GetType().GetProperty(value.GetType().Name + "Validator");                if (modelValidatorPropertyInfo != null)
                {                    
                    var modelValidator = modelValidatorPropertyInfo.GetValue(this.ValidatorHub) as IValidator;                    var validateResult = modelValidator.Validate(value);                    if (!validateResult.IsValid)
                    {                        
                          this.ValidatorErrorHandler(validateResult);
                        existError = true;
                    }

                }
            }            
            if (existError)
            {
                ViewData["Error"] = DicError;
                filterContext.Result = View();
            }            
            base.OnActionExecuting(filterContext);
        }

在 OnActionExecuting 里,我们首先定义了一个existError,用来判断是否验证失败的,然后我们遍历了 filterContext.ActionParameters.Values

在filterContext 里,我们看到ActionParameters 是关于Action的参数的,通过调试我发现,他是一个集合,其键是参数名,比如拿我们这个Person来讲。

 [HttpPost]        
     public ActionResult ValidatorTest(Person model)
        {            
        // 
            //  一大堆代码            
            //            
                return Redirect("https://www.baidu.com");
        }

filterContext.ActionParameters 集合里就有一个数据,其键是"model" 值呢 model

所以呢我们通过遍历filterContext.ActionParameters.Value 就能取出每一个Action的所有参数了,而每一个参数通过.getType().Name 则能取出他的类型名,比如这里model类型是Person 所以filterContext.ActionParameters["model"].GetType().Name 就是“Person”了。

知道了实体是什么类型,如何获取具体验证器呢?想想我们的验证器配置 Person = PersonValidator 那太简单了,这不是一对一的关系嘛,但总不可能通过一个switch 去工厂返回吧,那这样还需要维护一个工厂方法。当然不是咯,这就要用到咱.NET 提供的强大反射技术

有时候我们有一个匿名对象,是一个object的时候,又不知道它具体是什么类型,如何取它的属性呢?

我这有一个方法,他能解决这个问题。

考拉新媒体导航
考拉新媒体导航

考拉新媒体导航——新媒体人的专属门户网站

下载

 public static class ReflectHelper
    {        
        public static object GetPropertyByAnonymousObject(string propertyName, object obj)
        {            
            var property = obj.GetType().GetProperties().Where(p => p.Name == propertyName).FirstOrDefault();            if (property == null)
            {                
                throw new Exception(string.Format("{0}对象未定义{1}属性", nameof(obj), nameof(propertyName)));
            }            
            return property.GetValue(obj);
        }
    }

从使用上,传递一个属性名和对象进来,返回一个object的属性。

我们看看内部都做了些什么。

首先获取类型,然后获取执行的属性,诶,这个属性可不是真的属性哦,这个是PropertyInfo类型,是反射里的数据类型,它不是真正的属性值,但我们如果想要获取真正的属性值怎么办呢?其实只需要调用他的GetValue就行了,他有一个参数,这个参数是指获取那个对象上的属性,于是把object传进去就行。

有了这个基础,反观我们的目的,知道了Person,有一个对象叫ValidatotHub 里面有个属性PersonValidator ,所以我们只需要获取一个叫ValidatorHub对象里的PersonValidator属性就行了。(Person是可替换的,是根据参数类型来的,前面已经解释过了,这里以Person举例)

现在有个问题了,我们取到的PersonValidator 是一个object类型的,object类型我可不好使用啊,我们又不能显示的转换为具体类型,因为谁知道具体类型是啥呢。如果写死了就凉了。那肯定也不能用个switch来维护啊,那样不又增加工作量了吗。

我们慢慢发现PersonValidator继承自AbstractValidator 很显然它的基类也需要一个具体类型,不行,继续往上走,诶,发现了AbstractValidator继承自IValidator,并且IValidator定义了Validate方法。这不就好了吗,我as 为IValidator类型,就可以用了。这里使用了(里氏转换原则)。我尽量写得通俗易懂,也将许多基础东西提一下,但不肯能面面俱到,所以还是建立在一部分基础之上的。(当然更重要的一点是,通过这次遇到的问题让我以后在设计泛型类结构的时候,都要去继承一个非泛型的接口,如果FluentValidator没有继承自IValidator 而只是继承自IValidator其实从简单使用上来讲,并没有什么影响啊,但到了我们刚刚这里,问题就出来了,所以这也是给我们狠狠地上了一课啊)

现在我就可以在这里进行验证了,我们知道value 就是那个model 所以直接对他进行验证,验证会返回一个ValidationResult类型接下来的事我就不解释了,相信上一章已经讲得很清楚了。最后根据是否存在错误在进行提前处理,如果有错误的话就直接返回视图呈现错误了,连咱们的Action都不执行了。好了,到这里咱们昨天讲得OnActionExecuted 可以直接Delete拉 。

我们现在把ValidatorTest里的验证代码都去掉来测试一下。

        [HttpPost]        
        public ActionResult ValidatorTest(Person model)
        {            
            // 
                //  一大堆代码            
                //            
                return Redirect("https://www.baidu.com");
        }

在 ValidatorTest 里打上断点,然后什么都不填,直接提交。

断点没触发,但错误消息已呈现。多试几次~.

同样没触发。

那我们来一次正确的验证。

 

断点触发了。并且值都通过了校验

F5放行,最终我们的页面跳转到了 www.baidu.com。

好了,小伙伴们还不快去改改代码造就幸福生活。

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
PHP高性能API设计与Laravel服务架构实践
PHP高性能API设计与Laravel服务架构实践

本专题围绕 PHP 在现代 Web 后端开发中的高性能实践展开,重点讲解基于 Laravel 框架构建可扩展 API 服务的核心方法。内容涵盖路由与中间件机制、服务容器与依赖注入、接口版本管理、缓存策略设计以及队列异步处理方案。同时结合高并发场景,深入分析性能瓶颈定位与优化思路,帮助开发者构建稳定、高效、易维护的 PHP 后端服务体系。

32

2026.03.04

AI安装教程大全
AI安装教程大全

2026最全AI工具安装教程专题:包含各版本AI绘图、AI视频、智能办公软件的本地化部署手册。全篇零基础友好,附带最新模型下载地址、一键安装脚本及常见报错修复方案。每日更新,收藏这一篇就够了,让AI安装不再报错!

23

2026.03.04

Swift iOS架构设计与MVVM模式实战
Swift iOS架构设计与MVVM模式实战

本专题聚焦 Swift 在 iOS 应用架构设计中的实践,系统讲解 MVVM 模式的核心思想、数据绑定机制、模块拆分策略以及组件化开发方法。内容涵盖网络层封装、状态管理、依赖注入与性能优化技巧。通过完整项目案例,帮助开发者构建结构清晰、可维护性强的 iOS 应用架构体系。

58

2026.03.03

C++高性能网络编程与Reactor模型实践
C++高性能网络编程与Reactor模型实践

本专题围绕 C++ 在高性能网络服务开发中的应用展开,深入讲解 Socket 编程、多路复用机制、Reactor 模型设计原理以及线程池协作策略。内容涵盖 epoll 实现机制、内存管理优化、连接管理策略与高并发场景下的性能调优方法。通过构建高并发网络服务器实战案例,帮助开发者掌握 C++ 在底层系统与网络通信领域的核心技术。

25

2026.03.03

Golang 测试体系与代码质量保障:工程级可靠性建设
Golang 测试体系与代码质量保障:工程级可靠性建设

Go语言测试体系与代码质量保障聚焦于构建工程级可靠性系统。本专题深入解析Go的测试工具链(如go test)、单元测试、集成测试及端到端测试实践,结合代码覆盖率分析、静态代码扫描(如go vet)和动态分析工具,建立全链路质量监控机制。通过自动化测试框架、持续集成(CI)流水线配置及代码审查规范,实现测试用例管理、缺陷追踪与质量门禁控制,确保代码健壮性与可维护性,为高可靠性工程系统提供质量保障。

77

2026.02.28

Golang 工程化架构设计:可维护与可演进系统构建
Golang 工程化架构设计:可维护与可演进系统构建

Go语言工程化架构设计专注于构建高可维护性、可演进的企业级系统。本专题深入探讨Go项目的目录结构设计、模块划分、依赖管理等核心架构原则,涵盖微服务架构、领域驱动设计(DDD)在Go中的实践应用。通过实战案例解析接口抽象、错误处理、配置管理、日志监控等关键工程化技术,帮助开发者掌握构建稳定、可扩展Go应用的最佳实践方法。

60

2026.02.28

Golang 性能分析与运行时机制:构建高性能程序
Golang 性能分析与运行时机制:构建高性能程序

Go语言以其高效的并发模型和优异的性能表现广泛应用于高并发、高性能场景。其运行时机制包括 Goroutine 调度、内存管理、垃圾回收等方面,深入理解这些机制有助于编写更高效稳定的程序。本专题将系统讲解 Golang 的性能分析工具使用、常见性能瓶颈定位及优化策略,并结合实际案例剖析 Go 程序的运行时行为,帮助开发者掌握构建高性能应用的关键技能。

50

2026.02.28

Golang 并发编程模型与工程实践:从语言特性到系统性能
Golang 并发编程模型与工程实践:从语言特性到系统性能

本专题系统讲解 Golang 并发编程模型,从语言级特性出发,深入理解 goroutine、channel 与调度机制。结合工程实践,分析并发设计模式、性能瓶颈与资源控制策略,帮助将并发能力有效转化为稳定、可扩展的系统性能优势。

47

2026.02.27

Golang 高级特性与最佳实践:提升代码艺术
Golang 高级特性与最佳实践:提升代码艺术

本专题深入剖析 Golang 的高级特性与工程级最佳实践,涵盖并发模型、内存管理、接口设计与错误处理策略。通过真实场景与代码对比,引导从“可运行”走向“高质量”,帮助构建高性能、可扩展、易维护的优雅 Go 代码体系。

40

2026.02.27

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
国外Web开发全栈课程全集
国外Web开发全栈课程全集

共12课时 | 1万人学习

进程与SOCKET
进程与SOCKET

共6课时 | 0.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号