现在的位置: 首页 > 综合 > 正文

一行代码搞定你的QueryString!(原创)

2013年09月14日 ⁄ 综合 ⁄ 共 13993字 ⁄ 字号 评论关闭
Web开发做得多了,总觉得是体力活,于是搞些代码让自己脱离无聊的Coding吧(是脱离“无聊的”Coding,不是脱离无聊的“Coding”)。
 

初级阶段

为每个QueryString写转换的代码,针对不同的类型,进行转换和错误处理。
 

中级阶段

写了一个函数,专门做转换(1.1里写的):

  /// <summary>
  
/// Convert query string to parameter.
  
/// </summary>
  
/// <param name="name">Name of query string</param>
  
/// <param name="defaultValue">Default value of query string</param>
  
/// <param name="isRequired">If the query string is required</param>
  private object ConvertParameter(string name, object defaultValue, bool isRequired)

高级阶段

昨天写的,第一次发文,大家拍砖吧:
 
主要是用了Attribute和反射的思想,首先给变量设置HttpQueryString的属性,绑定上相应的QueryString,然后由Page基类来读取相应的QueryString信息。

属性这么写(HttpQueryStringAttribute.cs):

using System;

namespace GooKuu.Framework.Web
{

    /// <summary>
    
/// Specifies a field for a query string. 
    
/// </summary>
    [AttributeUsage(AttributeTargets.Field)]
    
public sealed class HttpQueryStringAttribute : Attribute
    {
        
private string _name;
        
private object _defaultValue;
        
private bool _isRequired;

        /// <summary>
        
/// Constructor. The query string must be provided.
        
/// </summary>
        
/// <param name="name">Name of the query string</param>
        public HttpQueryStringAttribute(string name)
        {
            _name 
= name;
            _defaultValue 
= null;
            _isRequired 
= true;
        }

        /// <summary>
        
/// Constructor. If the query string is not be provided, using the default value.
        
/// </summary>
        
/// <param name="name">Name of the query string</param>
        
/// <param name="defaultValue">Default value of the query string which is not provided</param>
        public HttpQueryStringAttribute(string name, object defaultValue)
        {
            _name 
= name;
            _defaultValue 
= defaultValue;
            _isRequired 
= false;
        }

        /// <summary>
        
/// Name of the query string.
        
/// </summary>
        public string Name
        {
            
get { return _name; }
        }

        /// <summary>
        
/// Default value of the query string which is not provided.
        
/// </summary>
        public object DefaultValue
        {
            
get { return _defaultValue; }
        }

        /// <summary>
        
/// Indicates if the query string must be provided.
        
/// </summary>
        public bool IsRequired
        {
            
get { return _isRequired; }
        }
    }
}

页面基类是这样的(PageBase.cs):

using System;
using System.Reflection;
using System.Web;
using System.Web.UI;

namespace GooKuu.Framework.Web
{
    
/// <summary>
    
/// Base class of all pages.
    
/// </summary>
    public class PageBase : Page
    {
        
/// <summary>
        
/// Override OnLoad method of base class.
        
/// </summary>
        
/// <param name="e"></param>
        protected override void OnLoad(System.EventArgs e)
        {
            ParameterInitialize();
            
base.OnLoad(e);
        }

        /// <summary>
        
/// Initialize parameters according to query strings.
        
/// </summary>
        private void ParameterInitialize()
        {
            
// Get Type of current page class.
            Type type = this.GetType();

            // Get all fields of current page class.
            FieldInfo[] fields = type.GetFields();

            foreach (FieldInfo field in fields)
            {
                
// Get HttpQueryStringAttribute of current field.
                HttpQueryStringAttribute attribute = (HttpQueryStringAttribute)Attribute.GetCustomAttribute(field, typeof(HttpQueryStringAttribute));

                // If has HttpQueryStringAttribute, this field is for a query string.
                if (attribute != null)
                {
                    SetField(field, attribute);
                }
            }
        }

        /// <summary>
        
/// Set field according to the HttpQueryStringAttribute.
        
/// </summary>
        
/// <param name="field">The field will be set</param>
        
/// <param name="attribute">The attribute of current field</param>
        private void SetField(FieldInfo field, HttpQueryStringAttribute attribute)
        {
            
// The query string must be provided.
            if (attribute.IsRequired)
            {
                
if (Request.QueryString[attribute.Name] != null)
                {
                    SetFieldValue(field, 
this, attribute.Name, field.FieldType);
                }
                
else
                {
                    
throw new Exception(string.Format("Query string /"{0}/" is required", attribute.Name), new NullReferenceException());
                }
            }
            
// If the query string is not be provided, using the default value.
            else
            {
                
if (attribute.DefaultValue == null || field.FieldType == attribute.DefaultValue.GetType())
                {
                    
if (Request.QueryString[attribute.Name] == null || Request.QueryString[attribute.Name] == string.Empty)
                    {
                        field.SetValue(
this, attribute.DefaultValue);
                    }
                    
else
                    {
                        SetFieldValue(field, 
this, attribute.Name, field.FieldType);
                    }
                }
                
else
                {
                    
throw new Exception(string.Format("Invalid default value of query string /"{0}/"({1})", attribute.Name, field.Name), new NullReferenceException());
                }
            }
        }

        /// <summary>
        
/// Set the value of current field according to the query string.
        
/// </summary>
        
/// <param name="field">The field will be set</param>
        
/// <param name="obj">The object whose field value will be set</param>
        
/// <param name="name">The name of query string</param>
        
/// <param name="conversionType">The type to be converted</param>
        private void SetFieldValue(FieldInfo field, object obj, string name, Type conversionType)
        {
            
try
            {
                
// Set field value.
                field.SetValue(obj, Convert.ChangeType(Request.QueryString[name], conversionType));
            }
            
catch (Exception ex)
            {
                
throw new Exception(string.Format("The given value of query string /"{0}/" can not be convert to {1}", name, conversionType), ex);
            }
        }
    }
}

在页面里,这样写就OK了(Default.aspx.cs):

using System;
using System.Data;
using System.Configuration;
using System.Collections;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using GooKuu.Framework.Web;

public partial class _Default : PageBase
{
    
    
/// <summary>
    
/// Name 是Query String的名字,"Anonymous"是缺省值。
    
/// 如果没有提供这个Query String,就采用缺省值。
    
/// </summary>
    [HttpQueryString("Name""Anonymous")]
    
public string name;

    /// <summary>
    
/// UserId 是Query String的名字,不提供缺省值。
    
/// 如果没有提供这个Query String或者提供的格式不正确导致转换失败,都会抛出异常。
    
/// </summary>
    [HttpQueryString("UserId")]
    
public int userId;

    protected void Page_Load(object sender, EventArgs e)
    {
        Response.Write(
string.Format("Name is {0}<br/>", name));
        Response.Write(
string.Format("UserId is {0}<br/>", userId));
    }
}

posted on 2006-01-16 11:01 Ariel Y. 阅读(3042) 评论(22)  编辑 收藏 引用 收藏至365Key 所属分类: Technology


评论:

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 11:21 | rexsp
// Get all fields of current page class.
FieldInfo[] fields = type.GetFields();
感觉没有必要把所有的栏位读出来循环,只读那些加载了自定义的Attribute的就好了吧  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 11:33 | Ariel Y.
@rexsp

我也是这么想的,可是我不知道GetFields应该传什么参数才能得到只加载了自定义的Attribute的Field。能指教一下用哪个BindingFlags枚举吗?  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 13:24 | kwklover
提两个问题:

1,写.net的文章,是否应该注明适用于.net2.0还是.net1.x,毕竟.net发展已经5年多了,平台版本之间具有一定差异性 ?

2,是否应该说明这种方法有什么好处和坏处 ?
  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 13:41 | Ariel Y.
@kwklover

谢谢你的意见,确实应该写清楚的。

我没有注明,应该是通用的:)因为没有用到什么.net 2.0的新特性,不过我确实是在VS 2005里写的。

好处么,应该是方便吧,减少我们的代码量,脱离一部分枯燥重复的Coding。
坏处么,应该是性能问题吧,但是我也不知道用反射会对性能造成多大影响,有时间测试一下;这里还希望高手指点一下性能的影响。

好多朋友说看不懂:(其实本来这篇文章就不是什么入门文章,也不是学习新知识,只是介绍我的一个思路,没有教大家写代码。所以,没有做过多解释。注释已经写的很清楚了。
  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 14:15 | mkimtaehee
不懂,未来应该要学习的。(最近到处听说动态类构造啊,反射什么的)
我的技术还停留在WINFROM的数据库 增删改查 真觉得自己低级 汗~~~~~~~~~~~~~~~~~~~~~  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 15:23 | 午夜寻欢
终于学习到怎么用自定义属性了!

可我还是不太明白你这些代码要完成的功能是什么?
难道就是简单的类型转换吗?
有这个必要写这么长的代码吗?  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 17:01 | Ariel Y.
我这段代码可以省去以后在页面里处理Query String的代码。

比如以前写:

public Guid userId;
public int count;

public void Page_Load()
{
try
{
userId = new Guid(Request.QueryString["UserId"].ToString());
}
catch
{
throw new Exception();
}

if(Request.QueryString["Count"] == null || Request.QueryString["Count"].ToSTring() = string.Empty)
{
count = 0;
}
else
{
try
{
count = Convert.ToInt32(Request.QueryString["Count"].ToString());
}
catch
{
throw new Exception();
}
}
}

现在只要写:

[HttpQueryString("UserId")]
public Guid userId; // 这里的字段不能是private的。

[HttpQueryString("Count", 0)]
public int count; // 这里的字段不能是private的。

就可以了。

难道你只看到了类型转换?
怎么说也是根据定义的变量的数据类型自动转换对应的QueryString吧?呵呵。

我觉得“有这个必要写这么长的代码吗?”这种思想是很有害的一种想法。
假设我这个代码有100行,我一次就写这么多了,以后每次就只有几行。如果不这么干(我是说,不提炼一些东西出来),你每页都要写,假设20行,你写一个项目我想也不仅仅只有5个页面吧?你看,做一个项目我就比你快:)
就像我开头说的,做一些枯燥重复的开发是没有意义的。永远是软件工人。

  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 17:47 | lee_j
我看完楼主的代码后,发现从设计上有一个失误.PageBase类是实现代码复用的关键,而用自定义Atribute反而有画蛇添足的感觉.正如楼主所说利用反射机制令代码的效率降低.
我想有两种方法去改进
1.把PageBase的SetField功能移到HttpRequestStringAttribute类中;
2.不用自动义Attribute,改成Interface.PageBase去实现Interface,而不用反射机制.
  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 22:35 | 木野狐
我认为这种实现方法过于麻烦。我的实现通常都是这样的:
在一个公共的方法类里面这样写,
public class Util {
private Util() {}

// 从 querystring 集合中安全的取得一个 string. (总是不会有 null,所以叫做 'Safe')
public static string GetStringSafeFromQueryString(Page page, string key) {
string value = page.Request.QueryString[key];
return (value == null) ? string.Empty : value;
}

// 在上述基础上,实现几个常用类型的获取方法。
public static int GetInt32SafeFromQueryString(Page page, string key, int defaultValue) {
string value = GetStringSafeFromQueryString(page, key);
int i = defaultValue;
try {
i = int.Parse(value);
} catch {}
return i;
}
// double 的实现
public static double GetDoubleSafeFromQueryString(Page page,
string key, double defaultValue) {
string value = GetStringSafeFromQueryString(page, key);
double d = defaultValue;
try {
d = double.Parse(value);
} catch {}
return d;
}
// 同理可以写出 float, ... 的实现
}

在我的任何页面里面,要获取 querystring 的时候,只要这样就可以了:
比如我要获取一个 string:
string name = Util.GetStringSafeFromQueryString(this, "name");
if (name.Length > 0) {
// 进行正常的处理...
} else {
// 不处理。
}

获取 int:
int id = Util.GetInt32SafeFromQueryString(this, "id", 0);

处理 double, float 等等方法完全一样。

我认为就一个 QueryString 的处理没必要上升到反射的高度,其实有时候反过来想想,实现的那么复杂也许会丧失一定的灵活性。比如我某个值忽然要改为从 Form 里面得到呢?从 Session 得到呢?这时候就可以比较出哪种做法更适合敏捷的适应需求。

页面里的 QueryString 的处理,之所以大家都很痛恨手工写编码,无非是因为这么几点:
1. 需要判断是否 == null 才敢用 .ToString(), 很不爽。稍微不注意,就会抛出异常导致页面错误。
2. 整形,double 等值类型从 QueryString 里面获取的时候,不知道是否为合法的数值,因此 Parse 的时候总是要写重复的处理异常的代码。

归纳一下,其实,一个页面用下列语法获取一个 QueryString 的时候,他得到的是如下东西:
(假设用 string name = Request.QueryString["name"]; 来获取。)
1. ...test.aspx 得到 null
2. ...test.aspx?name= 得到 ""
3. ...test.aspx?name=abc 得到 "abc"

我认为 1 和 2 的情况实际上从程序处理上来讲,是没有区别的。因此判断 null 没有必要,我们每次都将 null 的值自动让他转为 string.Empty 应该是比较安全的做法。

基于上述理由,我觉得如我上面所写的简单的工具类,就可以轻量级的解决安全的读取 QueryString 的问题。

有不对的地方,请大家多多指教。  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-16 23:35 | 双鱼座
呵呵,这种方式与我从前做权限控制的方式几乎一样,用来处理QueryString的确小题大作了一点。两处缺陷:一是污染了Page类的代价太大了一点(自然木野孤的方案清洁一点);二是必须定义一个Field并且要加上自定义标签并且要与QueryString键字符串相吻合。
我在控制权限的时候,那个自定义标签是抽象的,加在Page固有的Field上(通常是页面中静态的Control),对于不同的Control根本不同的权限采取不同的策略进行不同的处理,具有充分的可扩充性,这样污染Page类就物有所值。  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-17 09:19 | Terrylee
楼主的设计思想很好……

可是为了QueryString花这么大的代价,值得吗?  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-17 10:12 | Ariel Y.
@各位

有人说这方法画蛇添足,有人说麻烦有,人说污染,有人说小题大做。我的想法呢,很简单,前边已经说过,我是为了少写代码,越少越好,呵呵。让机器多干点,人少干点,没错吧?其实,我也有个前提,做的一些项目都是小系统,在局域网内运行,一个站点跑一个Xeon的Server。我何苦折磨自己去讨好Xeon呢?麻烦?谁麻烦?我们麻烦还是机器麻烦?机器麻烦?干吗不麻烦它?几个G的脑瓜天天在那儿傻着!

对于Large-scale网站,我才不会用这个,性能即过程!

这么多高人关注,谢谢,呵呵。下边的言语如果多有得罪,还请见谅!

@lee_j

第一点,我和你想的一样,但是我不会,能指点一下吗?在Attribute里可以读到被它绑定的对象吗?我没找到方法。

第二点,没明白你的意思,Interface来做什么?但是好像对于我这篇文章就跑题了,我这篇文章叫一行代码搞定QueryString,呵呵。

请指教!

@木野狐

你这类代码的思想,正是我现在用的。不过我觉得完全没有必要传那个Page进去,用HttpContext.Current.Request.QueryString[]就可以读了

而且现在我有两种实现,一个是你这种Utility类,一种更OO的方法是自己实现一个QueryString类作为Page基类的成员,用的时候:this.QueryString.GetInt32(......);

@双鱼座

你的文章我很喜欢,但是先说一句得罪了!我有些出离愤怒了!

第一,怎么就叫代价大了?怎么就小了?
第二,必须定义一个Feild..............................。难道其他方式不用定义变量吗?其他方式不更是要写代码吗?其他方式不需要与QueryString键字符串相吻合吗?难道一行代码都不用写就让我实现吗?我可做不到:(

为什么你的污染就值?怎么污染就不值?太主观了吧?我地孩儿也是孩儿啊,555555。

@Terrylee & All

其实我这篇文章讨论的问题真是芝麻绿豆大点子的事儿,纯属是玩些技术把戏,我也就是写着玩儿玩儿。但是真的写好了,并且简单好用,何乐而不用呢?
  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-17 10:27 | 木野狐
楼主的出发点很好,从技术的角度讲也是一种很有益的练习,不过双鱼座所说的“污染”其实只是从 OO 思想的角度来讲的,并没有任何贬低的意思啊。还望楼主不要误解了。常常我们会说“污染”一个名称空间之类的说法,其实说的不过是某个类,或者某个名称空间下干了比它应有的职责更多的事情而已,呵呵。

关于我的回复,昨天回复完后,也贴到自己的 blog 上去了:
http://rchen.cnblogs.com/archive/2006/01/16/318561.html

根据 stone790809 网友的建议,我现在已经改进了做法,不再传递 Page 类作为参数了,这个跟你的建议是一致的 :)
其实并非我不知道 HttpContext.Current.Request 这个用法,只是平常传递 Page 对象传惯了,写成了惯性而已,呵呵。
  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-17 10:40 | Ariel Y.
@木野狐

哦,才发现,确实,从OO的角度讲,确实有污染,谢谢指正!

@双鱼座

不好意思啦。  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-17 17:13 | zkxp
什么是OO到处看到,汗一个先。。。  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-17 22:55 | sema
国外有类似思路的文章介绍,大家可以参考一下
http://www.codeproject.com/aspnet/WebParameter.asp
如果英文的看着累,也可以看看翻译的文章
http://dragon.cnblogs.com/archive/2005/03/24/125160.html
应该说这种思路还是很有意思的。但是实际对效率的影响的确需要评估。
  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-18 00:22 | Ariel Y.
@sema

您的名字让我想起一句广告:“穿什么,就是什么”:)

楼上吓我一身汗,跟我的好像!我可是干巴巴想出来的,呵呵。

不过我喜欢这种说法:“但是实际对效率的影响的确需要评估”,而不是有些人(没有特指,请勿对号入座,真的):“效率很差,效率不高”。

知道读数据DataReader比DataAdpater+DataSet快多少吗?总有人说快好多,实际的评测结果好像是14%-18%(参考自“DEV411 ASP.NET: Best Practices For Performance - Stephen Walther www.SuperexpertTraining.com - TechEd 2004”)

@zhxp

OO means Object-Oriented Programming Concepts
  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-01-19 12:35 | torome
看了高人的文章.
自己感觉还有很多要学习。.
努力!~  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-20 15:10 | Stanley Liu
搞明白自定义属性了,happying
谢谢Ariel Y. ~  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-01-23 08:59 | xiao_p
能不能把鼠标特殊效果撤销掉了……
实在是难受,看得时候……

呵呵

对于文章,就没有什么好说的了,确实不错~~
  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-02-14 10:00 | chating
是个好方法,但程序代码效率低.  回复
  
# re: 一行代码搞定你的QueryString!(原创) 2006-04-21 08:13 | 生活被我强奸。。。
呵呵。。这么一大堆我不太明白@我来这里是谢谢你的:)。。

  回复
  

# re: 一行代码搞定你的QueryString!(原创) 2006-04-21 14:33 | Ariel Y.

抱歉!评论已关闭.