一. 前世今生

  乍眼一看,该标题写的有点煽情,最近也是在不断反思,怎么能把博客写好,让人能读下去,通俗易懂,深入浅出。

  接下来几个章节都是围绕框架本身提供特性展开,有MVC程序集提供的,也有其它程序集提供;在本章节将重点介绍几个MVC框架提供的且作用于方法上的特性,并且模仿其源码自定义特性。

  其实早在前面的 DotNet进阶章节,就写过一篇关于特性的文章了,这里重新总结一下特性核心要点。

     1. 什么是特性?

   特性是一个类,在不影响程序封装的情况下,额外的给程序添加一些信息,用于运行时描述程序或者影响程序的行为。

  2. 特性的作用范围?

   提到特性的作用范围,就不得不提到 AttributeUsage了,该类本身就是一个特性,继承了Attribute类,用于约束自定义特性(你可以看到系统提供的很多特性中,均能看到它的身影),下面先看一        下它的源码:

    

   该特性有一个参数,两个核心属性,AttributeTargets参数约束了该给特性可以作用的范围,通过右面的代码可知,可以作用于:类、方法、参数、属性、返回值等等,该参数默认为ALL。

    AllowMultiple:约束该特性能否同时作用于某个元素(类、方法、属性等等)多次,默认为false。

Inherited:约束该特性作用于基类(或其它)上,其子类能否继承该特性,默认为true。

  3. 如何自定义特性?

    简单来说,声明一个以Attribute结尾的类,继承Attribute类,然后加上AttributeTargets特性约束,一个简单的特性就产生了。

二. MVC中的常用特性

  有了前面的铺垫,这里讲解【System.Web.Mvc】程序集下的一些特性就很好理解,理解源码的同时,主要掌握其如何使用。

    MVC中提供的常用特性有:【HttpGet】、【HttpPost】、【AcceptVerbs】、【ActionName】、【NoAction】、【AllowAnonymous】、【ValidateAntiForgeryToken】、【ChildActionOnly】、【Bind】这九个特性。

  查看源码可知,其中【HttpGet】【HttpPost】【AcceptVerbs】【ActionName】【NoAction】【ChildActionOnly】均继承ActionNameSelectorAttribute类,实现了IsValidForRequest这个抽象方法,且特性约束为: [AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = true)],显然这些特性均是作用于方法上的,且不需要同时作用,允许子类继承该特性。

  以【HttpGet】的源码为例:

  其中【AllowAnonymous】直接继承Attribute类,源码如下:

  

  其中【ValidateAntiForgeryToken】继承了FilterAttribute类,实现了IAuthorizationFilter接口,源码如下:

  (扩展一下:AuthorizeAttribute类也是继承了FilterAttribute类,实现了IAuthorizationFilter接口)

1. HttpGet和HttpPost

(1).  HttpGet:只允许Get请求访问

底层运用的AcceptVerb特性实现的,所以等价于[AcceptVerbs(HttpVerbs.Get)]或[AcceptVerbs("Get")]

测试:前端用Ajax请求,如果非Get请求方式进行请求,则提示404找不到

(2).  HttpPost:只允许Post请求访问

底层运用的AcceptVerb特性实现的,所以等价于[AcceptVerbs(HttpVerbs.Post)]或[AcceptVerbs("Post")]

测试:前端用Ajax请求,如果非Post请求方式进行请求,则提示404找不到

 特别注意:如果一个方法要同时允许Get和Post请求[HttpGet]和[HttpPost]同时加载上面是错误的!!这个时候就需要使用AcceptVerb特性了(当然方法上如果什么特性也不加,什么请求均支持)

2. AcceptVerbs

AccetpVerbs:用于限定请求方式(包括:Get、Post、Put、Delete、Head、Patch、Options)

查看源码可知:该特性有两个构造函数,所有两种写法,如:只允许Get请求,可以:[AcceptVerbs(HttpVerbs.Get)]和[AcceptVerbs("Post")]

若要同时支持多种请求,可以:[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]或[AcceptVerbs("Get", "Post")]

相关测试代码如下:

        //1. 下面三种写法均为只允许Get请求
//[HttpGet]
//[AcceptVerbs(HttpVerbs.Get)]
//[AcceptVerbs("Get")] //2. 下面三种写法均为只允许Get请求
//[HttpPost]
//[AcceptVerbs(HttpVerbs.Post)]
//[AcceptVerbs("Post")] //3. 下面两种写法表示:既允许Get请求也允许Post请求
[AcceptVerbs(HttpVerbs.Get | HttpVerbs.Post)]
//[AcceptVerbs("Get", "Post")]
public ActionResult TestMethordWay()
{
return Content("请求成功");
}
         //1. 测试只允许Get或Post请求
$("#btn1").click(function () {
$.ajax({
type: "Put", //Post 、Put
url: "TestMethordWay",
data: "",
success: function (msg) {
alert(msg);
}
});
});

3. ActionName

ActionName:修改Action本身的方法名

测试:请求TestActionName1方法,报404找不到

请求TestActionName2方法,正常访问

相关测试代码如下:

         /// <summary>
/// 名字变为:TestActionName2了
/// </summary>
/// <returns></returns>
[ActionName("TestActionName2")]
public ActionResult TestActionName1()
{
return Content("我是TestActionName1");
}
    //2. 测试ActionName特性
$("#btn2").click(function () {
$.ajax({
type: "Get",
url: "TestActionName2", //TestActionName1
data: "",
success: function (msg) {
alert(msg);
}
});
});

4. NoAction

NoAction: 标记控制器中的action将不在是一个方法,不能前端Http请求访问

测试:前端页面ajax进行请求,报404找不到

     但在其它action中进行调用,能正常调用

相关测试代码如下:

        /// <summary>
/// 标记该方法将不是一个方法
/// </summary>
/// <returns></returns>
[NonAction]
public ActionResult TestNoAction()
{
return Content("请求成功");
}
   //3. 测试NoAction特性
$("#btn3").click(function () {
$.ajax({
type: "Get",
url: "TestNoAction", //TestNoAction
data: "",
success: function (msg) {
alert(msg);
}
});
});

5. AllowAnonymous

AllowAnonymous:该特性用于标记在授权期间要跳过 AuthorizeAttribute 过滤器的验证

解释:AuthorizeAttribute是MVC框架自带的实现IAuthorizationFilter过滤器的一个类,内部有一套自身业务验证(感兴趣的可以自己研究源码)

而AllowAnonymous就是为了标记跨过AuthorizeAttribute验证的

这里不做详细测试

6. ValidateAntiForgeryToken

ValidateAntiForgeryToken:阻止跨站请求伪造攻击(CSRF).

①. CSRF原理是什么:

  a.用户mr访问正规网站A,登录验证通过,并在用户mr处产生Cookie

  b.用户mr在不关闭A网站的情况下打开危险的B网站,在B网站中要求访问A网站,发出一个Request请求

  c.这时候浏览器带着A网站在mr出产生的Cookie进行访问A网站

  d.这时候A网站就无法判断这个cookie是谁产生的,默认就给通过了

详细见:https://www.cnblogs.com/hechunming/p/4647646.html

②:解决方案

  a. 在Controller中的action上加上特性[ValidateAntiForgeryToken]

  b. 对于增删改查操作前端调用: $.ajaxAntiForgery方法进行ajax请求(需要引入jqueryToken的js文件)

这里不做详细测试

7. ChildActionOnly

ChildActionOnly:限制操作方法只能由子操作进行调用。

  ①. 测试直接输入:http://localhost:7559/SpecialAttribute/Index2, 无法访问报错.

  ②. 需要通过RenderAction来调用(存在问题,与Unity改造框架冲突冲突)

这里RenderAction不做详细测试

8. Bind

①. 源码的角度分析:该特性可以作用于类或参数(本章节测试作用于参数)

②. 该特性有三个核心属性:

  a. Exclude:获取或设置不允许绑定的属性名称的列表(各属性名称之间用逗号分隔)

  b. Include:获取或设置允许绑定的属性名称的列表(各属性名称之间用逗号分隔),与Exclude一个道理,通常根据情况使用一个即可

  c. Prefix:获取或设置在呈现表示绑定到操作参数或模型属性的标记时要使用的前缀

不适用与ajax提交,适用于razor语法中的@{Html.TextBox(stu.id)},在现在前后端分离盛行的情况下,有点不适合了

测试:

  前端过个ajax传过来三个参数:id、name、sex, 参数中的stu只能收到id和name,sex为null,若想收到sex,需要在方法中通过request进行接收

         /// <summary>
/// stu中的sex属性为null
/// var 中sex为男
/// </summary>
/// <param name="stu"></param>
/// <returns></returns>
public ActionResult TestBindAttribute([Bind(Include = "id,name")]Student stu)
{
var sex = Request["sex"];
return Content("请求成功");
}
          //4.测试BindAttribute特性
$("#btn4").click(function () {
$.ajax({
type: "Get",
url: "TestBindAttribute",
data: {"id":"123","name":"ypf","sex":"男"},
success: function (msg) {
alert(msg);
}
});
});

三. 自定义一个类似的特性

要求:支持Get请求,且必须是Ajax请求

思路:由HttpGet特性可以知道:需要继承ActionMethodSelectorAttribute类,然后覆写IsValidForRequest方法即可

     public class HttpGetAndAjaxAttribute : ActionMethodSelectorAttribute
{
public override bool IsValidForRequest(ControllerContext controllerContext, MethodInfo methodInfo)
{
//1.获取请求方式
string HttpWay = controllerContext.HttpContext.Request.GetHttpMethodOverride(); //2. 获取是否是Ajax请求
bool isAjax = controllerContext.HttpContext.Request.IsAjaxRequest(); if (HttpWay.ToLower() == "get" && isAjax == true)
{
return true;
}
return false; }
} [HttpGetAndAjax]
public ActionResult GetAndAjax()
{
return Content("请求成功");
}
         //5.测试自定义特性
$("#btn5").click(function () {
$.ajax({
type: "Get",
url: "GetAndAjax",
data: "",
success: function (msg) {
alert(msg);
}
});
});

第九节:从源码的角度分析MVC中的一些特性及其用法的更多相关文章

  1. 从源码的角度分析ViewGruop的事件分发

    从源码的角度分析ViewGruop的事件分发. 首先我们来探讨一下,什么是ViewGroup?它和普通的View有什么区别? 顾名思义,ViewGroup就是一组View的集合,它包含很多的子View ...

  2. 从Android源码的角度分析Binder机制

    欢迎访问我的个人博客,原文链接:http://wensibo.top/2017/07/03/Binder/ ,未经允许不得转载! 前言 大家好,好久不见,距离上篇文章已经有35天之久了,因为身体不舒服 ...

  3. 第74讲:从Spark源码的角度思考Scala中的模式匹配

    今天跟随王老师学习了从源码角度去分析scala中的模式匹配的功能.让我们看看源码中的这一段模式匹配: 从代码中我们可以看到,case RegisterWorker(id,workerHost,.... ...

  4. 从源码的角度分析List与Set的区别

    很多时候我们在讨论List与Set的异同点时都在说: 1.List.Set都实现了Collection接口 2.List是有序的,可以存储重复的元素,允许存入null 3.Set是无序的,不允许存储重 ...

  5. [转]Android事件分发机制完全解析,带你从源码的角度彻底理解(上)

    Android事件分发机制 该篇文章出处:http://blog.csdn.net/guolin_blog/article/details/9097463 其实我一直准备写一篇关于Android事件分 ...

  6. 从源码的角度解析View的事件分发

    有好多朋友问过我各种问题,比如:onTouch和onTouchEvent有什么区别,又该如何使用?为什么给ListView引入了一个滑动菜单的功能,ListView就不能滚动了?为什么图片轮播器里的图 ...

  7. 【转】Android事件分发机制完全解析,带你从源码的角度彻底理解(下)

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/9153761 记得在前面的文章中,我带大家一起从源码的角度分析了Android中Vi ...

  8. 从源码的角度解析Mybatis的会话机制

    坐在我旁边的钟同学听说我精通Mybatis源码(我就想不通,是谁透漏了风声),就顺带问了我一个问题:在同一个方法中,Mybatis多次请求数据库,是否要创建多个SqlSession会话? 可能最近撸多 ...

  9. Android AsyncTask完全解析,带你从源码的角度彻底理解

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/11711405 我们都知道,Android UI是线程不安全的,如果想要在子线程里进 ...

随机推荐

  1. Spring Boot 正常启动后访问Controller提示404

    问题描述 今天重新在搭建Spring Boot项目的时候遇到访问Controller报404错误,之前在搭建的时候没怎么注意这块.新创建项目成功后,作为项目启动类的Application在com.bl ...

  2. 英语口语练习系列-C06-购物

    <水调歌头>·苏轼 明月几时有,把酒问青天. 不知天上宫阙,今夕是何年? 我欲乘风归去,又恐琼楼玉宇, 高处不胜寒. 起舞弄清影,何似在人间! 转朱阁,低绮户,照无眠. 不应有恨,何事长向 ...

  3. 【Python 25】52周存钱挑战5.0(datetime库和import)

    1.案例描述 按照52周存钱法,存钱人必须在一年52周内,每周递存10元.例如,第一周存10元,第二周存20元,第三周存30元,直到第52周存520元. 记录52周后能存多少钱?即10+20+30+. ...

  4. c++11のunique_lock和once_flag

    一. Unique _lock和lockguard一样,到那时比lockguard更加灵活,可以随时按照需要加锁开锁 std::unique_lock<std::mutex> locker ...

  5. mysql partition分区

    (转) 自5.1开始对分区(Partition)有支持 = 水平分区(根据列属性按行分)=举个简单例子:一个包含十年发票记录的表可以被分区为十个不同的分区,每个分区包含的是其中一年的记录. === 水 ...

  6. R语言学习——处理数据对象的实用函数

    length(object) # 显示对象中元素/成分的数量 dim(boject) # 显示某个对象的维度 str(object) # 显示某个对象的结构 class(object) # 显示某个对 ...

  7. ESP8266产品ID

    ESP.getChipId() https://github.com/espressif/arduino-esp32/blob/master/libraries/ESP32/examples/Chip ...

  8. 【Swift】iOS开发笔记(二)

    前言 这个系列主要是一些开发中遇到的坑记录分享,有助于初学者跨过这些坑,攒够 7 条发一篇. 声明  欢迎转载,但请保留文章原始出处:)  博客园:http://www.cnblogs.com 农民伯 ...

  9. openstack第四章:neutron— 网络服务

    第四篇neutron— 网络服务 一.neutron 介绍:   Neutron 概述 传统的网络管理方式很大程度上依赖于管理员手工配置和维护各种网络硬件设备:而云环境下的网络已经变得非常复杂,特别是 ...

  10. mariadb集群配置(主从和多主)

    mariadb主从 主从多用于网站架构,因为主从的同步机制是异步的,数据的同步有一定延迟,也就是说有可能会造成数据的丢失,但是性能比较好,因此网站大多数用的是主从架构的数据库,读写分离必须基于主从架构 ...