ASP.NET MVC后台参数验证的几种方式

前言

参数验证是一个常见的问题,无论是前端还是后台,都需对用户输入进行验证,以此来保证系统数据的正确性。对于web来说,有些人可能理所当然的想在前端验证就行了,但这样是非常错误的做法,前端代码对于用户来说是透明的,稍微有点技术的人就可以绕过这个验证,直接提交数据到后台。无论是前端网页提交的接口,还是提供给外部的接口,参数验证随处可见,也是必不可少的。总之,一切用户的输入都是不可信的。

参数验证有许多种方式进行,下面以mvc为例,列举几种常见的验证方式,假设有一个用户注册方法

[HttpPost]
public ActionResult Register(RegisterInfo info)

一、通过 if-if 判断  

if(string.IsNullOrEmpty(info.UserName))

{

  return FailJson("用户名不能为空");

}

if(string.IsNullOrEmpty(info.Password))

{

  return FailJson("用户密码不能为空")

}

逐个对参数进行验证,这种方式最粗暴,但当时在WebForm下也确实这么用过。对于参数少的方法还好,如果参数一多,就要写n多的if-if,相当繁琐,更重要的是这部分判断没法重用,另一个方法又是这样判断。

二、通过 DataAnnotation

mvc提供了DataAnnotation对Action的Model进行验证,说到底DataAnnotation就是一系列继承了ValidationAttribute的特性,例如RangeAttribute,RequiredAttribute等等。ValidationAttribute 的虚方法IsValid 就是用来判断被标记的对象是否符合当前规则。asp.net mvc在进行model binding的时候,会通过反射,获取标记的ValidationAttribute,然后调用 IsValid 来判断当前参数是否符合规则,如果验证不通过,还会收集错误信息,这也是为什么我们可以在Action里通过ModelState.IsValid判断Model验证是否通过,通过ModelState来获取验证失败信息的原因。例如上面的例子:

public class RegisterInfo

{

  [Required(ErrorMessage="用户名不能为空")]

  public string UserName{get;set;}

 [Required(ErrorMessage="密码不能为空")]

  public string Password { get; set; }

}

事实上在webform上也可以参照mvc的实现原理实现这个过程。这种方式的优点的实现起来非常优雅,而且灵活,如果有多个Action共用一个Model参数的话,只要在一个地方写就够了,关键是它让我们的代码看起来非常简洁。

不过这种方式也有缺点,通常我们的项目可能会有很多的接口,比如几十个接口,有些接口只有两三个参数,为每个接口定义一个类包装参数有点奢侈,而且实际上为这个类命名也是非常头疼的一件事。

三、DataAnnotation 也可以标记在参数上

通过验证特性的AttributeUsage可以看到,它不只可以标记在属性和字段上,也可以标记在参数上。也就是说,我们也可以这样写:

public ActionResult Register([Required(ErrorMessage="用户名不能为空")]string userName, [Required(ErrorMessage="密码不能为空")]string password) 

这样写也是ok的,不过很明显,这样写很方法参数会难看,特别是在有多个参数,或者参数有多种验证规则的时候。

四、自定义ValidateAttribute

我们知道可以利用过滤器在mvc的Action执行前做一些处理,例如身份验证,授权处理的。同理,这里也可以用来对参数进行验证。FilterAttribute是一个常见的过滤器,它允许我们在Action执行前后做一些操作,这里我们要做的就是在Action前验证参数,如果验证不通过,就不再执行下去了。

定义一个BaseValidateAttribute基类如下:

public class BaseValidateAttribute : FilterAttribute

{

  protected virtual void HandleError(ActionExecutingContext context)

  {

    for (int i = ValidateHandlerProviders.Handlers.Count; i > 0; i--)

    {

      ValidateHandlerProviders.Handlers[i - 1].Handle(context);

      if (context.Result != null)

      {

        break;

      }

    }

  }

}

HandleError 用于在验证失败时处理结果,这里ValidateHandlerProviders提过IValidateHandler用于处理结果,它可以在外部进行注册。IValidateHandler定义如下:

public interface IValidateHandler

{

  void Handle(ActionExecutingContext context);

}

ValidateHandlerProviders定义如下,它有一个默认的处理器。

public class ValidateHandlerProviders

{

  public static List<IValidateHandler> Handlers { get; private set; }

  static ValidateHandlerProviders()

  {

    Handlers = new List<IValidateHandler>()

    {

      new DefaultValidateHandler()

    };

  }

  public static void Register(IValidateHandler handler)

  {

    Handlers.Add(handler);

  }

}  

这样做的目的是,由于我们可能有很多具体的ValidateAttribute,可以把这模块独立开来,而把最终的处理过程交给外部决定,例如我们在项目中可以定义一个处理器:

public class StanderValidateHandler : IValidateHandler

{

  public void Handle(ActionExecutingContext filterContext)

  {

    filterContext.Result = new StanderJsonResult()

    {

      Result = FastStatnderResult.Fail("参数验证失败", 555)

    };

  }

}

然后再应用程序启动时注册:ValidateHandlerProviders.Handlers.Add(new StanderValidateHandler());

举个两个栗子:

ValidateNullttribute:

public class ValidateNullAttribute : BaseValidateAttribute, IActionFilter

{

  public bool ValidateEmpty { get; set; }

  public string Parameter { get; set; }

  public ValidateNullAttribute(string parameter, bool validateEmpty = false)

  {

    ValidateEmpty = validateEmpty;

    Parameter = parameter;

  }

  public void OnActionExecuting(ActionExecutingContext filterContext)

  {

    string[] validates = Parameter.Split(',');

    foreach (var p in validates)

    {

      string value = filterContext.HttpContext.Request[p];

      if(ValidateEmpty)

      {

        if (string.IsNullOrEmpty(value))

        {

          base.HandleError(filterContext);

        }

      }

      else

      {

        if (value == null)

        {

          base.HandleError(filterContext);

        }

      }

    }

  }

  public void OnActionExecuted(ActionExecutedContext filterContext)

  {

  }

}

ValidateRegexAttribute:

 public class ValidateRegexAttribute : BaseValidateAttribute, IActionFilter

{

  private Regex _regex;

  public string Pattern { get; set; }

  public string Parameter { get; set; }

  public ValidateRegexAttribute(string parameter, string pattern)

  {

    _regex = new Regex(pattern);

    Parameter = parameter;

  }

  public void OnActionExecuting(ActionExecutingContext filterContext)

  {

    string[] validates = Parameter.Split(',');

    foreach (var p in validates)

    {

      string value = filterContext.HttpContext.Request[p];

      if (!_regex.IsMatch(value))

      {

        base.HandleError(filterContext);

      }

    }

  }

  public void OnActionExecuted(ActionExecutedContext filterContext)

  { 

  }

}

更多的验证同理实现即可。

这样,我们上面的写法就变成:

[ValidateNull("userName,password")]

public ActionResult Register(string userName, string password)

综合看起来,还是ok的,与上面的DataAnnotation可以权衡选择使用,这里我们可以扩展更多有用的信息,如错误描述等等。

总结

当然每种方式都有有缺点,这个是视具体情况选择了。一般参数太多建议就用一个对象包装了。

(0)

相关推荐

  • ASP.NET MVC自定义错误页面真的简单吗?

    如果你在设置asp.net mvc自定义错误页面时遇到问题,这并不止你一个人.惊讶之余你的做法是正确的,没有起到作用的原因是其一部分错误是由asp.net管道处理的,另一部分是由iis直接处理. 通常情况 (我期望是这种情况,在一些其他框架/服务器上) 我们只需要在一个地方配置自定义错误页就可以了,无论怎么哪儿引发的错误.就像这样︰ <customErrors mode="On"> <error code="404" path="404.

  • Asp.NET MVC中使用SignalR实现推送功能

    一.简介 Signal 是微软支持的一个运行在 Dot NET 平台上的 html websocket 框架.它出现的主要目的是实现服务器主动推送(Push)消息到客户端页面,这样客户端就不必重新发送请求或使用轮询技术来获取消息. 可访问其官方网站:https://github.com/SignalR/ 获取更多资讯. 二.Asp.net SignalR 是个什么东东 Asp.net SignalR是微软为实现实时通信的一个类库.一般情况下,SignalR会使用JavaScript的长轮询(lo

  • 灵活掌握Asp.net MVC中GridView的使用方法

    本文教程为大家分享了GridView控件的使用方法和具体实现代码,供大家参考,具体内容如下 Models文件下实体类: public class Customer { public int Id { get; set; } public string CompanyName { get; set; } public string ContactTitle { get; set; } public string Address { get; set; } public string City {

  • ASP.NET MVC使用EPPlus,导出数据到Excel中

    好久没写博客了,今天特地来更新一下,今天我们要学习的是如何导出数据到Excel文件中,这里我使用的是免费开源的Epplus组件. 源代码下载:https://github.com/caofangsheng93/ExcelExportInMvc 介绍 这篇文章,介绍的是怎样导出数据到Excel文件中,大多数的后端程序都有报表功能:把显示在Grid中的数据导出到Excel文件中,这篇文章中使用的是EPPlus组件. EPPlus是一个基于OOXML[Open Extended Markup Lang

  • ASP.NET MVC 微信JS-SDK认证

    ASP.NET MVC微信JS-SDK认证,具体内容: 写在前面 前阵子因为有个项目需要做微信自定义分享功能,因而去研究了下微信JS-SDK相关知识. 此文做个简单的记(tu)录(cao)... 开始 所有的东西都从文档开始:微信JSSDK说明文档 项目需要用到的是分享接口不过使用微信JS-SDK之前,需要做JS接口认证. 认证如下: 步骤一:绑定域名 步骤二:引入JS文件 步骤三:通过config接口注入权限验证配置 步骤四:通过ready接口处理成功验证 步骤五:通过error接口处理失败验

  • ASP.NET MVC阿里大于短信接口开发短信群发能

    互联网上有许多公司提供短信接口服务,诸如网易云信.阿里大于等等.我在自己项目里需要使用到短信服务起到通知作用,实际开发周期三天,完成配置.开发和使用,总的说,阿里大于提供的接口易于开发,非常的方便,短信费用是计数缴纳的,作为个人开发者,我使用的服务业务产生的费用为0.045¥/条(10万条以下). 现在要实现一个例会短信群发通知的功能,所有被通知对象信息均存于Mysql中,应用架构采用asp.net MVC .首先准备好获取的API各项(以下各项服务参数都需要在大于官网上申请), 申请好自己的短

  • ASP.NET MVC下的四种验证编程方式[续篇]

    在<ASP.NET MVC的四种验证编程方式>一文中我们介绍了ASP.NET MVC支持的四种服务端验证的编程方式("手工验证"."标注ValidationAttribute特性"."让数据类型实现IValidatableObject或者IDataErrorInfo"),那么在ASP.NET MVC框架内部是如何提供针对这四种不同编程方式的支持的呢?接下来我们就来聊聊这背后的故事. 一.ModelValidator与ModelVali

  • [Asp.Net MVC4]验证用户登录实现实例

    最近我们要做一个仿sina的微博,碰巧的是我最近在学习mvc,就想用mvc技术实现这个项目. 既然是微博,那不用想也应该知道肯定要有用户登陆,但是和常规的asp.NET登陆又不一样,以下是我一下午+一晚上的研究成果~~~ 首先,建好数据库以及表,这就不用说了吧. 下面说一下主要的结构 控制器: HomeController 这是主页的控制器 LoginController 这是登陆的控制器 类: CDBTemplate.cs 这是数据库数据对应的类,里边描述的是数据库的结构 //////////

  • 详解ASP.NET MVC的筛选器

    在ActionInvoker对Action的执行过程中,除了通过利用ActionDescriptor对Action方法的执行,以及之前进行的Model绑定与验证之外,还具有一个重要的工作,那就是对相关筛选器(Filter)的执行.ASP.NET MVC的筛选器是一种基于AOP(面向方面编程)的设计,我们将一些非业务的逻辑实现在相应的筛选器中,然后以一种横切(Crosscutting)的方式应用到对应的Action方法.当Action方法执行前后,这些筛选器会自动执行.ASP.NET MVC提供了

  • ASP.NET Core MVC 配置全局路由前缀

    ASP.NET Core MVC 配置全局路由前缀 前言 大家好,今天给大家介绍一个 ASP.NET Core MVC 的一个新特性,给全局路由添加统一前缀.严格说其实不算是新特性,不过是Core MVC特有的. 应用背景 不知道大家在做 Web Api 应用程序的时候,有没有遇到过这种场景,就是所有的接口都是以 /api 开头的,也就是我们的api 接口请求地址是像这样的: http://www.example.com/api/order/333 或者是这样的需求 http://www.exa

  • Asp.net MVC中获取控制器的名称的方法

    1.视图中 string controller = ViewContext.RouteData.Route.GetRouteData(this.Context).Values["controller"].ToString(); string controller = ViewContext.RouteData.Values["controller"].ToString(); 2.控制器的action中 string controller = RouteData.Ro

随机推荐