querystring字面上的意思就是查詢字符串,一般是對http請求所帶的數據進行解析。querystring模塊只提供4個方法:
- querystring.parse
- querystring.stringify
- querystring.escape
- querystring.unescape
這4個方法是相對應的。
首先,使用querystring模塊之前,需要require進來:
const querystring = require("querystring");
其次,就可以使用模塊下的方法了:
1 querystring.parse(str,separator,eq,options)
parse這個方法是將一個字符串反序列化為一個對象。
參數:
- str指需要反序列化的字符串;
- separator(可省)指用於分割str這個字符串的字符或字符串,默認值為"&";
- eq(可省)指用於划分鍵和值的字符或字符串,默認值為"=";
- options(可省)該參數是一個對象,里面可設置maxKeys和decodeURIComponent這兩個屬性:
- maxKeys:傳入一個number類型,指定解析鍵值對的最大值,默認值為1000,如果設置為0時,則取消解析的數量限制;
- decodeURIComponent:傳入一個function,用於對含有%的字符串進行解碼,默認值為querystring.unescape。在官方API的例子中,使用gbkDecodeURIComponent這個方法會報錯,顯示gbkDecodeURIComponent is no defined,這是因為在使用這個gbkDecodeURIComponent這個方法之前需要先進行定義。在API中也寫了Assuming gbkDecodeURIComponent function already exists...這句話的意思是”假設這個gbkDecodeURIComponent方法已經存在”。
例子1,querystring.parse
1 querystring.parse("name=whitemu&sex=man&sex=women"); 2 /* 3 return: 4 { name: 'whitemu', sex: [ 'man', 'women' ] } 5 */ 6 querystring.parse("name=whitemu#sex=man#sex=women","#",null,{maxKeys:2}); 7 /* 8 return: 9 { name: 'whitemu', sex: 'man' } 10 */
2 querystring.stringify(obj,separator,eq,options)
stringify這個方法是將一個對象序列化成一個字符串,與querystring.parse相對。
參數:
- obj指需要序列化的對象
- separator(可省)用於連接鍵值對的字符或字符串,默認值為"&";
- eq(可省)用於連接鍵和值的字符或字符串,默認值為"=";
- options(可省)傳入一個對象,該對象可設置encodeURIComponent這個屬性:
encodeURIComponent:值的類型為function,可以將一個不安全的url字符串轉換成百分比的形式,默認值為querystring.escape()。
例子2,querystring.stringify
querystring.stringify({name: 'whitemu', sex: [ 'man', 'women' ] }); /* return: 'name=whitemu&sex=man&sex=women' */ querystring.stringify({name: 'whitemu', sex: [ 'man', 'women' ] },"*","$"); /* return: 'name$whitemu*sex$man*sex$women' */
3 querystring.escape(str)
escape可使傳入的字符串進行編碼
例子3,querystring.escape
querystring.escape("name=慕白"); /* return: 'name%3D%E6%85%95%E7%99%BD' */
4 querystring.unescape(str)
unescape方法可將含有%的字符串進行解碼
例子4,querystring.unescape
querystring.unescape('name%3D%E6%85%95%E7%99%BD'); /* return: 'name=慕白' */
小結:
querystring這個模塊相對的還是比較簡單,僅有4個方法。
- querystring.stringify序列化;
- querystring.parse反序列化;
- querystring.escape編碼;
- querystring.unescape解碼;
進階版
可以寫一個函數,專門用於做轉換
private object ConvertParameter(string name, object defaultValue, bool isRequired)
我們甚至還可以把它抽象出來一個方法。
提取抽象方法
用Attribute和反射的思想,首先給變量設置HttpQueryString的屬性,綁定上相應的QueryString,然后由Page基類來讀取相應的QueryString信息。
屬性這么寫(HttpQueryStringAttribute.cs):
[AttributeUsage(AttributeTargets.Field)] public sealed class HttpQueryStringAttribute : Attribute { private string _name; private object _defaultValue; private bool _isRequired; public HttpQueryStringAttribute(string name) { _name = name; _defaultValue = null; _isRequired = true; } public HttpQueryStringAttribute(string name, object defaultValue) { _name = name; _defaultValue = defaultValue; _isRequired = false; } public string Name { get { return _name; } } public object DefaultValue { get { return _defaultValue; } } public bool IsRequired { get { return _isRequired; } } }
頁面基類是這樣的(PageBase.cs):
public class PageBase : Page { protected override void OnLoad(System.EventArgs e) { ParameterInitialize(); base.OnLoad(e); } private void ParameterInitialize() { Type type = this.GetType(); FieldInfo[] fields = type.GetFields(); foreach (FieldInfo field in fields) { HttpQueryStringAttribute attribute = (HttpQueryStringAttribute)Attribute.GetCustomAttribute(field, typeof(HttpQueryStringAttribute)); if (attribute != null) { SetField(field, attribute); } } } private void SetField(FieldInfo field, HttpQueryStringAttribute attribute) { 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()); } } 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()); } } } private void SetFieldValue(FieldInfo field, object obj, string name, Type conversionType) { try { 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):
public partial class _Default : PageBase { [HttpQueryString("Name", "Anonymous")] public string name; [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)); } }
討論
根據定義的變量的數據類型自動轉換對應的QueryString,可以省去以后在頁面里處理Query String的代碼。
反過來想,QueryString 的處理沒必要上升到反射的高度,復雜的實現也許會喪失一定的靈活性。比如我某個值忽然要改為從 Form 里面得到呢?從 Session 得到呢?這時候就可以比較出哪種做法更適合敏捷的適應需求。
頁面里的 QueryString 的處理,之所以大家都很痛恨手工寫編碼,無非是因為這么幾點:
- 需要判斷是否 == null 才敢用 .ToString(), 很不爽。稍微不注意,就會拋出異常導致頁面錯誤。
- 整形,double 等值類型從 QueryString 里面獲取的時候,不知道是否為合法的數值,因此 Parse 的時候總是要寫重復的處理異常的代碼。