惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V
Vulnerabilities – Threatpost
月光博客
月光博客
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
美团技术团队
Last Week in AI
Last Week in AI
Jina AI
Jina AI
P
Privacy International News Feed
有赞技术团队
有赞技术团队
WordPress大学
WordPress大学
宝玉的分享
宝玉的分享
T
Tenable Blog
阮一峰的网络日志
阮一峰的网络日志
P
Proofpoint News Feed
T
Tailwind CSS Blog
Apple Machine Learning Research
Apple Machine Learning Research
P
Privacy & Cybersecurity Law Blog
人人都是产品经理
人人都是产品经理
S
Schneier on Security
Google DeepMind News
Google DeepMind News
爱范儿
爱范儿
C
Cisco Blogs
K
Kaspersky official blog
C
Cybersecurity and Infrastructure Security Agency CISA
Hugging Face - Blog
Hugging Face - Blog
博客园_首页
O
OpenAI News
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
IT之家
IT之家
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
酷 壳 – CoolShell
酷 壳 – CoolShell
T
Tor Project blog
博客园 - 【当耐特】
腾讯CDC
V
V2EX
A
Arctic Wolf
Webroot Blog
Webroot Blog
S
Securelist
小众软件
小众软件
大猫的无限游戏
大猫的无限游戏
D
Darknet – Hacking Tools, Hacker News & Cyber Security
博客园 - 三生石上(FineUI控件)
The GitHub Blog
The GitHub Blog
量子位
J
Java Code Geeks
博客园 - 叶小钗
S
SegmentFault 最新的问题
Project Zero
Project Zero
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Scott Helme
Scott Helme
Cyberwarzone
Cyberwarzone

博客园 - sema

Netron开发快速上手(二):Netron序列化 发布一个免费开源软件-- PAD流程图绘制软件PADFlowChart Netron源码解读(一):GraphControl画布对象 Netron开发快速上手(一):GraphControl,Shape,Connector和Connection FileSystemWatcher事件多次触发的解决方法 IBatis.Net 中的数据类型转换 深入浅出之正则表达式(二) 深入浅出之正则表达式(一) 在.NET环境中实现每日构建(Daily Build)--NAnt篇 自动向网页Post信息并提取返回的信息 在C#中利用自动化模型操纵Word VS.NET 2003集成环境插件开发指南(三)----操纵VS开发环境(完结篇) VS.NET 2003集成环境插件开发指南(二)----使用窗口 VS.NET 2003集成环境插件开发指南(一)----操纵菜单 后期写作计划 开发Visual Studio风格的用户界面--MagicLibrary使用指南 Log4Net使用指南 一些写英文简历的词汇吧 关于一些大学课程名的中文翻译
利用反射解决QueryString和Session中的参数绑定问题
sema · 2005-03-24 · via 博客园 - sema

注:去年写的一篇文章了,现在把它贴出来,以做参考
这里是示例代码
利用反射解决
QueryStringSession中的参数绑定问题

一.     前言

本文主要译自网上的一篇文章(http://www.codeproject.com/aspnet/WebParameter.asp),自己又作了一些改进,因此形成了这篇心得。

二.     简介:

本文介绍了在ASP.NET页面对象中利用元数据自动从Page.Request对象的QueryString中填充页面的变量和属性的策略.

三.     问题的提出

当你对ASP.NET页面进行参数化时,比如:从Request.QueryStringGET)或Request.FormPOST)中获取参数,你需要花大量的时间写类似以下的代码:


但是你必须处理潜在的
FirstNamenull值是的情况,因此你可能会通过如下的代码提供一个缺省值

对于非字符串类型,你也可以通过稍微修改以上代码来决定当参数为null时是提供一个缺省值还是抛出一个异常,只是要用到Convert.ToXXX()函数

因此,在Page_Load函数里你可能会写如下的代码进行参数初始化

小结:

当你在处理从Request.QueryStringRequest.Form读取参数时,有以下的固定模式

  • Request.QueryString ( Request.Form)中检索值,
  • 当检索的值为空时抛出异常或提供一个缺省值
  • 将检索到的值转换成合适的数据成员(field)或属性(property
  • 如果检索值不能正确转换类型,抛出一个异常或提供一个缺省值

四.     解决方案:声明性参数绑定

l         表现形式:

一个解决的方案是使用元数据(metadata),并让一些函数执行实际的工作

比如说以下形式:

可选的构造函数提供了从Request.QueryString中查找参数的关键字,IsRequired属性决定了当该参数缺失时是否抛出异常。  

l         思路

本解决方案的思路是,利用反射来检索到附加在变量上的元数据,然后根据该元数据的属性决定如何对变量进行存取,其存取模式采用如第2节(《问题的提出》)最后一部分小结里的模式,只是改成了由程序自动判断进行。

1、  检索元数据属性

如前所述,当先定义一个从System.Attribute派生的元数据类WebParameterAttribut

注:AttributeUsageAttribute使用说明

说明:


    当以上面形式使用自定义的属性类时,实际上等与是生成了一个和LastName的类型对象LastName.GetType()相关联的WebParameterAttribute类的对象,通过LastName.GetType()对象可以检索到WebParameter对象的一些方法成员、数据成员以及属性等;

我们可以定义WebParameterAttribute类的静态方法,WebParameterAttribute.SetValues(object target, System.Web.HttpRequest request)完成自动初始化某个页面的所有参数,该方法有两个参数target表示将被自动进行参数化的对象例如可以是某个页面对象Page);request是在某个页面对象的初始化时传来的Request对象;后面我们会详细解释SetValues方法的设计


示例如下面粗体部分所示:


    在上面的示例中,静态方法WebParameterAttribut.SetValues先获取this指针代表的Page对象的类型对象this.GetType(),然后用该类型对象获取Page对象的所有变量和属性的信息,即可以获得并操纵Page对象的FirstNameLastNameCustomerID,也包括TestNum。然后SetValues方法检查每个检索到的变量和属性,看有没有关联WebParameter对象,相对来说,数据成员TestNum就没有关联WebParameter对象。

SetValues检测到变量(field)或属性(property)关联有WebParameter对象时,该方法会检索和变量或属性向关联的WebParameter对象的具体属性,比如说:


生成了一个WebParameter对象,并传递给构造函数一个参数”Last_Name”SetValues方法会检测出和string LastName相关联的WebParameter中包含的“Last_Name”并将其作为要在QueryString中检测的变量的名字。

l         代码解析

下面是SetValues的具体实现:

²        变量及属性定义

²        SetValues方法

request中检索特定值并设定target中指定变量和属性的值

²        SetValue方法

根据member对象的类型是变量(Field)或属性(Property),从request中检索特定值并设置target.member的值

public static void SetValue(MemberInfo member, object target, System.Web.HttpRequest request) 



         WebParameterAttribute[] attribs; 

         WebParameterAttribute attrib; 

         TypeConverter converter; 

         
string paramValue; 

         
string paramName; 

         
object typedValue; 

         
bool usingDefault; 

  

         
try 

         


                 attribs
=(WebParameterAttribute[])member.GetCustomAttributes(typeof(WebParameterAttribute), true); 

                 
//返回和变量或属性关联的WebAttribute对象或其子类的对象 

  

                
if(attribs!=null && attribs.Length>0

              


                        
//对于索引属性,不支持,所谓索引属性, 

                        
//是指C#中可以用类似数组下标形式访问的属性 

                         
if (member.MemberType==MemberTypes.Property) 

                      


                             ParameterInfo[] ps
=  ((PropertyInfo)member).GetIndexParameters(); 

                             
if (ps!=null && ps.Length>0

                                   
throw new NotSupportedException("Cannot apply WebParameterAttribute to indexed property"); 

                 }
 

  

                 
//因为WebAttribute在每个成员上只能应用一次, 

               
//所以取第一个就行了 

                attrib
=attribs[0]; 

                 
//获取[WebParameter(“Last_Name”)]中的”Last_Name”字符串 

                 
//如果是类似[WebParameter()] string Name;形式,则 

                 
//得到的名字是”Name”,和变量或属性的名字同名 

                paramName
=(attrib.ParameterName!=null)? attrib.ParameterName : member.Name; 

                 

                 
//下面将获取paramValue值,但是是string类型 

                 
//WebParameter自定义方法GetValue,后面有说明 

               
//从request对象获取QueryString[paramName]值 

               
//或是Form[paramName]值 

                 paramValue
=attrib.GetValue(paramName, request); 

  

                 
//如果在QueryString中未检索到paramName所对应的值 

                 usingDefault    
=false

                
if (paramValue==null

             


                     
if (attrib.DefaultValue!=null

                  
{//如果设定了缺省值,则用缺省值定赋值 

                         paramValue  
=attrib.DefaultValue; 

                         usingDefault    
=true

                     }
 

                     
else if (!attrib.IsRequired) 

                  
{//如果没有定义缺省值,且不要求一定有值,返回 

                         
return// 仅仅跳过该值 

                     }
 

                     
else 

                   
{//如果要求必须有值,且未检索到值,则抛出异常 

                         
throw new ApplicationException( 

                                    String.Format( 

                                           
"Missing required parameter '{0}'"

                                            paramName) 

                          ); 

                 }
 

  

                 
//下面将string类型的paramValue 

               
//根据变量或属性的类型作转换,存放在typedValue中 

                 
//对变量或属性,类型转换稍有不同 

  

                 

                
//根据成员是变量(FieldType),属性(PropertyType) 

               
//两种情况获取member的Type类型对象, 

               
//如果不是变量或属性,则抛出异常 

               
//注意:FiledInfo和PropertyInfo,MethodInfo 

               
//都是MemberInfo的子类 

               
//获取成员对应的类型转换器 

                converter
=TypeDescriptor.GetConverter( 

                            GetMemberUnderlyingType(member)); 

               
if(converter==null || !converter.CanConvertFrom(paramValue.GetType())) 

            
{//如果转换器为空或不能从字符串类型转换,抛出异常 

                           
throw new ApplicationException( 

                                    String.Format( 

                                           
"Could not convert from {0}"

                                            paramValue.GetType() 

                                    ) 

                        ); 

                  }
 

  

                 
try 

              
{//将paramValue从字符串类型转换成 

                     
//对应成员变量或属性的类型 

                     typedValue
=converter.ConvertFrom(paramValue); 

                     SetMemberValue(member, target, typedValue); 

                     
//注:自定义SetMemberValue,将target.member的值 

                     
//设为同类型的typedValue; 

                 }
 

                 
catch 

               


                     
// 这里捕获类型转换和在设置变量或属性中的异常错误 

                     
//如果定义了缺省值,且确定当发生无效值时使用缺省值 

                     
//设定变量或属性为缺省值 

                     
if (!usingDefault && attrib.IsDefaultUsedForInvalid && attrib.DefaultValue!=null

                   


                         typedValue  
=converter.ConvertFrom( attrib.DefaultValue); 

                         SetMemberValue(member, target, typedValue); 

                     }
 

                     
else 

                         
throw

                 }
 

             }
 

         }
 

         
catch(Exception err) 

         


             
throw new ApplicationException( "Property/field {0} could not be set from request - " + err.Message,err); 

          }
 

     }
 

  

²        GetValue方法

request中检索名paramName对应的值,返回字符串

²        GetMemberUnderlyingType方法

获取变量或属性对应的类型变量

²        SetMemberValue方法

根据变量或属性两种情况对成员进行赋值

五.     改进

为了实际解决QueryStringSession的参数化问题,对WebParameterAttribute类做了些修改,同时以WebParameterAttribute类为基类派生了QueryParameterAttribute类和SessionParameterAttribute,分别负责QueryStringSession的参数化。

具体做法如下:

WebParameterAttribute类中添加了两类静态函数,一类是用于从QueryStringSessionViewState中读取数据到Page类的成员变量中去。相应的实现函数分别是SetValuesFromBufferSetValueFromBufferSetMemberValue;另一类用于将Page类的成员变量值写入到SessionViewState的变量中去。相应的实现函数分别示SetValuesToBufferSetValueToBufferSetBufferValue。这两类函数的具体实现请看程序源代码,原理和前面讲述的一样。

六.     有待改进的地方

以参数BindingFlags.NonPublic调用Type.GetMembers方法时,系统规定反射代码只能存取被反射对象的protectedpublic访问类型的成员,要访问private成员,需要用到ReflectionPermission类赋于代码一定权限,而MSDN和其他的资料里关于如何使用ReflectionPermission类访问被反射对象的private成员语焉不详。所以目前本解决方案中还访问不到Page对象的private成员,而只能访问protectedpublic成员。

七.     技术文档

l         AttributeUsageAttribute

形如

MyTestvalue,attr1=value1,attr2=value2)]

的代码实际上是用类MyTestAttribute定义了一个对象MyTestAttribute(value),而value则是传递的构造函数的参数,可以不止一个,但是要按照定义构造函数时参数的顺序传递。而形如attr1=value1,attr2=value2,则是在MyTestAttribute类中定义了attr1,attr2属性(property),可以在生成MyTestAttribute的对象时给这些属性赋值,这是可选的。

因此,前面的代码表明了

[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple=false, Inherited=true)]

实际上是AttributeUsageAttribute类生成的一个对象,传递给构造函数的参数为AttributeTargets.Field | AttributeTargets.Property,同时给两个命名属性赋了值。

关于AttributeUsageAttributeMSDN里的说明是:定义您自己的特性类时,可通过在特性类上放置 AttributeUsageAttribute 来控制特性类的使用方式。指示的特性类必须直接或间接地从 Attribute 派生。

换句话说,我们定义一个自己的特性类WebParameterAttribute类时,要从Attribute派生,同时用AttributeUsageAttribute的对象来控制自己的WebParameterAttribute类将来可以怎样被使用。

向构造函数传递的参数为AttributeTargets类型的枚举变量,有以下几种组合:

Class:可以将自定义的特性类用于一个类

例:

Field:可以将自定义的特性类用于一个成员变量

例:

Property:可以将自定义的特性类用于一个属性

例:

[AttributeUsage(AttributeTargets.Property)]
public class PropertyTargetAttribute : Attribute
{
}
 
[PropertyTarget]
public string Name
{
     get{return m_name;}
     set{m_name=value;}
}

AttributeUsageAttribute的两个命名参数AllowMultiple=false, Inherited=true,其中AllowMultiple是指是否允许在同一个对象上多次使用自定义的属性类,而Inherited指明该属性是否可以有派生类:

例如:

[AttributeUsage(AttributeTargets.PropertyAllowMultiple=false)]
public class PropertyTargetAttributestring Name : Attribute
{
}
//错误的用法,因为AllowMultiple指明了
//不允许在同一对象上多次使用该属性
[PropertyTarget(John)]
[PropertyTarget(Tom) ]
class TestClass
{
}

l         获取类的类型信息

ToSetValues

1、             Type. GetMembers

当在派生类中重写时,使用指定绑定约束,搜索为当前 Type 定义的成员。

public abstract MemberInfo[] GetMembers(
   BindingFlags bindingAttr
);

参数

可以使用下列 BindingFlags 筛选标志定义包含在搜索中的字段: 
为了获取返回值,必须指定 BindingFlags.Instance  BindingFlags.Static 
指定 BindingFlags.Public 可在搜索中包含公共字段。 
指定 BindingFlags.NonPublic 可在搜索中包含非公共字段(即私有字段和受保护的字段)。 
指定 BindingFlags.FlattenHierarchy 可包含层次结构上的静态成员。 
下列 BindingFlags 修饰符标志可用于更改搜索的执行方式: 
BindingFlags.DeclaredOnly,表示仅搜索在 Type 上声明的字段,
而不搜索简单继承的字段。 
注意 
  必须与 Public  NonPublic 一起指定 Instance  Static,否则将不返回成员。
有关更多信息,请参见 System.Reflection.BindingFlags

2、             MemberInfo. GetCustomAttributes

To:SetValue

在派生类中被重写时,返回由 Type 标识的自定义特性的数组。

public abstract object[] GetCustomAttributes(
   Type attributeType,
   bool inherit
);

参数

attributeType 
要搜索的属性类型。只返回可分配给此类型的属性。 
inherit 
指定是否搜索该成员的继承链以查找这些属性。