初级阶段
中级阶段
/// 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)
高级阶段
属性这么写(HttpQueryStringAttribute.cs):
{
/// <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.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.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));
}
}
谢谢你的意见,确实应该写清楚的。
我没有注明,应该是通用的:)因为没有用到什么.net 2.0的新特性,不过我确实是在VS 2005里写的。
好处么,应该是方便吧,减少我们的代码量,脱离一部分枯燥重复的Coding。
坏处么,应该是性能问题吧,但是我也不知道用反射会对性能造成多大影响,有时间测试一下;这里还希望高手指点一下性能的影响。
好多朋友说看不懂:(其实本来这篇文章就不是什么入门文章,也不是学习新知识,只是介绍我的一个思路,没有教大家写代码。所以,没有做过多解释。注释已经写的很清楚了。
回复
比如以前写:
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个页面吧?你看,做一个项目我就比你快:)
就像我开头说的,做一些枯燥重复的开发是没有意义的。永远是软件工人。
在一个公共的方法类里面这样写,
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 的问题。
有不对的地方,请大家多多指教。 回复
我在控制权限的时候,那个自定义标签是抽象的,加在Page固有的Field上(通常是页面中静态的Control),对于不同的Control根本不同的权限采取不同的策略进行不同的处理,具有充分的可扩充性,这样污染Page类就物有所值。 回复
有人说这方法画蛇添足,有人说麻烦有,人说污染,有人说小题大做。我的想法呢,很简单,前边已经说过,我是为了少写代码,越少越好,呵呵。让机器多干点,人少干点,没错吧?其实,我也有个前提,做的一些项目都是小系统,在局域网内运行,一个站点跑一个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
其实我这篇文章讨论的问题真是芝麻绿豆大点子的事儿,纯属是玩些技术把戏,我也就是写着玩儿玩儿。但是真的写好了,并且简单好用,何乐而不用呢?
回复
关于我的回复,昨天回复完后,也贴到自己的 blog 上去了:
http://rchen.cnblogs.com/archive/2006/01/16/318561.html
根据 stone790809 网友的建议,我现在已经改进了做法,不再传递 Page 类作为参数了,这个跟你的建议是一致的
其实并非我不知道 HttpContext.Current.Request 这个用法,只是平常传递 Page 对象传惯了,写成了惯性而已,呵呵。
回复
http://www.codeproject.com/aspnet/WebParameter.asp
如果英文的看着累,也可以看看翻译的文章
http://dragon.cnblogs.com/archive/2005/03/24/125160.html
应该说这种思路还是很有意思的。但是实际对效率的影响的确需要评估。
回复
您的名字让我想起一句广告:“穿什么,就是什么”:)
楼上吓我一身汗,跟我的好像!我可是干巴巴想出来的,呵呵。
不过我喜欢这种说法:“但是实际对效率的影响的确需要评估”,而不是有些人(没有特指,请勿对号入座,真的):“效率很差,效率不高”。
知道读数据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
回复