一步一步Asp.Net MVC系列_權限管理之權限控制
在權限管理中一個很重要的就是關於權限的攔截驗證問題,特別是我們在webform中的驗證,比純winform要更復雜,winform可以通過驗證把按鈕隱藏或者禁用的方式,但是在web中我們不能僅僅通過隱藏按鈕,不顯示菜單/按鈕之類的手段,因為客戶端的代碼都是透明的,如果我們不在服務端把好關,那么權限根本就無從談起,我們必須徹底的進行驗證,每一步動作都要進行驗證,客戶端的每一個ajax提交都要進行驗證,如果任何一個ajax 動作都做過驗證了,那么至少可以保證基本的安全性了.
在純webform中,我們通常怎么來進行權限控制呢?
一般情況下,設計基類然后,在基類寫好驗證方法,子類調用並驗證
我們來看看啟航動力的開源CMS怎么進行的驗證:
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Web;
5: using System.Web.UI.WebControls;
6: using DTcms.Common;
7:
8: namespace DTcms.Web.UI
9: {
10: public class ManagePage : System.Web.UI.Page
11: {
12: protected internal Model.siteconfig siteConfig;
13:
14: public ManagePage()
15: {
16: this.Load += new EventHandler(ManagePage_Load);
17: siteConfig = new BLL.siteconfig().loadConfig(Utils.GetXmlMapPath("Configpath"));
18: }
19:
20: private void ManagePage_Load(object sender, EventArgs e)
21: {
22: //判斷管理員是否登錄
23: if (!IsAdminLogin())
24: {
25: Response.Write("<script>parent.location.href='" + siteConfig.webpath + siteConfig.webmanagepath + "/login.aspx'</script>");
26: Response.End();
27: }
28: }
29:
30: #region 管理員============================================
31: /// <summary>
32: /// 判斷管理員是否已經登錄(解決Session超時問題)
33: /// </summary>
34: public bool IsAdminLogin()
35: {
36: //如果Session為Null
37: if (Session[DTKeys.SESSION_ADMIN_INFO] != null)
38: {
39: return true;
40: }
41: else
42: {
43: //檢查Cookies
44: string adminname = Utils.GetCookie("AdminName", "DTcms"); //解密用戶名
45: string adminpwd = Utils.GetCookie("AdminPwd", "DTcms");
46: if (adminname != "" && adminpwd != "")
47: {
48: BLL.manager bll = new BLL.manager();
49: Model.manager model = bll.GetModel(adminname, adminpwd);
50: if (model != null)
51: {
52: Session[DTKeys.SESSION_ADMIN_INFO] = model;
53: return true;
54: }
55: }
56: }
57: return false;
58: }
59:
60: /// <summary>
61: /// 取得管理員信息
62: /// </summary>
63: public Model.manager GetAdminInfo()
64: {
65: if (IsAdminLogin())
66: {
67: Model.manager model = Session[DTKeys.SESSION_ADMIN_INFO] as Model.manager;
68: if (model != null)
69: {
70: return model;
71: }
72: }
73: return null;
74: }
75:
76: /// <summary>
77: /// 檢查管理員權限
78: /// </summary>
79: /// <param name="channel_id">頻道ID</param>
80: /// <param name="action_type">操作類型</param>
81: public void ChkAdminLevel(int channel_id, string action_type)
82: {
83: Model.manager model = GetAdminInfo();
84: BLL.manager_role bll = new BLL.manager_role();
85: bool result = bll.Exists(model.role_id, channel_id, action_type);
86: if (!result)
87: {
88: string msbox = "parent.f_errorTab(\"錯誤提示\", \"您沒有管理該頁面的權限,請勿嘗試非法進入!\")";
89: //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true); //修正BUG
90: Response.Write("<script type=\"text/javascript\">" + msbox + "</script>");
91: Response.End();
92: }
93: }
94:
95: /// <summary>
96: /// 檢查管理員權限
97: /// </summary>
98: /// <param name="channel_name">欄目名稱</param>
99: /// <param name="action_type">操作類型</param>
100: public void ChkAdminLevel(string channel_name, string action_type)
101: {
102: Model.manager model = GetAdminInfo();
103: BLL.manager_role bll = new BLL.manager_role();
104: bool result = bll.Exists(model.role_id, channel_name, action_type);
105: if (!result)
106: {
107: string msbox = "parent.f_errorTab(\"錯誤提示\", \"您沒有管理該頁面的權限,請勿嘗試非法進入!\")";
108: //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true); //修正BUG
109: Response.Write("<script type=\"text/javascript\">" + msbox + "</script>");
110: Response.End();
111: }
112: }
113:
114: /// <summary>
115: /// 檢查管理員權限
116: /// </summary>
117: /// <param name="channel_name">欄目名稱</param>
118: /// <param name="action_type">操作類型</param>
119: /// <returns>bool</returns>
120: public bool IsAdminLevel(string channel_name, string action_type)
121: {
122: Model.manager model = GetAdminInfo();
123: BLL.manager_role bll = new BLL.manager_role();
124: return bll.Exists(model.role_id, channel_name, action_type);
125: }
126:
127: #endregion
128:
129: #region 枚舉==============================================
130:
131: /// <summary>
132: /// 統一管理操作枚舉
133: /// </summary>
134: public enum ActionEnum
135: {
136: /// <summary>
137: /// 所有
138: /// </summary>
139: All,
140: /// <summary>
141: /// 查看
142: /// </summary>
143: View,
144: /// <summary>
145: /// 添加
146: /// </summary>
147: Add,
148: /// <summary>
149: /// 修改
150: /// </summary>
151: Edit,
152: /// <summary>
153: /// 刪除
154: /// </summary>
155: Delete
156: }
157:
158: /// <summary>
159: /// 屬性類型枚舉
160: /// </summary>
161: public enum AttributeEnum
162: {
163: /// <summary>
164: /// 輸入框
165: /// </summary>
166: Text,
167: /// <summary>
168: /// 下拉框
169: /// </summary>
170: Select,
171: /// <summary>
172: /// 單選框
173: /// </summary>
174: Radio,
175: /// <summary>
176: /// 復選框
177: /// </summary>
178: CheckBox
179: }
180: #endregion
181:
182: #region JS提示============================================
183:
184: /// <summary>
185: /// 添加編輯刪除提示
186: /// </summary>
187: /// <param name="msgtitle">提示文字</param>
188: /// <param name="url">返回地址</param>
189: /// <param name="msgcss">CSS樣式</param>
190: protected void JscriptMsg(string msgtitle, string url, string msgcss)
191: {
192: string msbox = "parent.jsprint(\"" + msgtitle + "\", \"" + url + "\", \"" + msgcss + "\")";
193: ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox, true);
194: }
195:
196: /// <summary>
197: /// 帶回傳函數的添加編輯刪除提示
198: /// </summary>
199: /// <param name="msgtitle">提示文字</param>
200: /// <param name="url">返回地址</param>
201: /// <param name="msgcss">CSS樣式</param>
202: /// <param name="callback">JS回調函數</param>
203: protected void JscriptMsg(string msgtitle, string url, string msgcss, string callback)
204: {
205: string msbox = "parent.jsprint(\"" + msgtitle + "\", \"" + url + "\", \"" + msgcss + "\", " + callback + ")";
206: ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox, true);
207: }
208: #endregion
209:
210: }
211: }
在子類校驗的時候繼承ManagePage的基類,然后就這樣校驗:
可以看到這種是通常的設計方案,基類定義,子類校驗,不過這個啟航動力CMS,他完全是在基類定義枚舉,控制僅僅停留在增刪改查,瀏覽,這些,不過CMS確實這一層就可以了,而且它的設計感覺不太好,因為到處都是驗證代碼,每一個增刪改查方法都有這些驗證的代碼,如果是我我就會用委托的方式綁定方法,在Page_Load里面驗證權限,委托綁定增刪改查方法,這樣,把驗證的過程大大節省,這些甚至可以設計成公共的方法,來實現.
我們今天講解的是MVC里面的權限驗證,MVC天然的Controller,Action,讓我們的權限控制更加容易,而且更簡潔,更清晰.
首先,先來看看,MVC里面的一個小特色設計:
這個是一個普通的model,在mvc示例項目中,他采用Attribute方式來驗證,我當時看到的時候感覺耳目一新,以前看<<CLR VIA C#>> Attribute的時候,當時還在想這些東西可以干什么??僅僅是元數據描述??后來才發現,這東西太強大了,控件的設計,反射,ORM,無處不在,Attributes也讓代碼更加優雅.
我們可以在mvc中定義各種Attribute來讓我們的代碼更優雅,而且讓權限更簡潔,比如我們經常會設計,
xxxx頁面登錄后才能訪問,
xxxxx頁面匿名用戶也能訪問,
xxxx頁面必須經過權限驗證才能訪問,
xxxxx各種類型
我們可以定義各種Attribute來描述它,這種描述也讓權限控制更加優雅.
比如:我們有幾個登錄頁面,錯誤顯示頁面等等,這些頁面,可以單獨設計一個Attribute標記來標識.
我們可以定義各種各樣的標記來描述權限控制,
可以看到我們在這里只需要標記匿名標記,然后在權限攔截中進行處理,如果存在Anonymous標記就默認允許.
這里在公共基類中設計驗證標記,子類繼承,這樣,我們就可以達到子類全部都需要驗證處理..
接下來,我們就看看,我們的權限攔截如何處理的.
我們的權限攔截是通過繼承ActionFilterAttribute,自定義攔截器,這里參考傳說中弦哥的思路.
簡單地說,ActionFilter就是Action過濾器,任何一個請求都像篩子一樣的過濾.所以,這個Filter就是篩子,我們需要的就是在這個篩子上做權限控制,這樣我們的權限不就容易得多了么?
這時候,我想到了以前的一張asp.net生命周期,一個請求過來同樣經過了HttpModule等一層層,在那里做權限攔截,可能效果更好,如果webform設計,可能要嘗試一下看看能不能這么做.
1: /// <summary>
2: /// 權限攔截
3: /// </summary>
4: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
5: public class PermissionFilterAttribute : ActionFilterAttribute
6: {
7: /// <summary>
8: /// 權限攔截
9: /// </summary>
10: /// <param name="filterContext"></param>
11: public override void OnActionExecuting(ActionExecutingContext filterContext)
12: {
13: //權限攔截是否忽略
14: bool IsIgnored = false;
15: if (filterContext == null)
16: {
17: throw new ArgumentNullException("filterContext");
18: }
19: var path = filterContext.HttpContext.Request.Path.ToLower();
20: //獲取當前配置保存起來的允許頁面
21: IList<string> allowPages = ConfigSettings.GetAllAllowPage();
22: foreach (string page in allowPages)
23: {
24: if (page.ToLower() == path)
25: {
26: IsIgnored = true;
27: break;
28: }
29: }
30: if (IsIgnored)
31: return;
32: //接下來進行權限攔截與驗證
33: object[] attrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ViewPageAttribute), true);
34: var isViewPage = attrs.Length == 1;//當前Action請求是否為具體的功能頁
35:
36: if (this.AuthorizeCore(filterContext) == false)//根據驗證判斷進行處理
37: {
38: //注:如果未登錄直接在URL輸入功能權限地址提示不是很友好;如果登錄后輸入未維護的功能權限地址,那么也可以訪問,這個可能會有安全問題
39: if (isViewPage == true)
40: {
41: //跳轉到登錄頁面
42: filterContext.RequestContext.HttpContext.Response.Redirect("~/Admin/Manage/UserLogin");
43: }
44: else
45: {
46:
47: //跳轉到登錄頁面
48: filterContext.RequestContext.HttpContext.Response.Redirect("~/Admin/Manage/Error");
49: }
50: }
51: }
這個Attribute直接借鑒的弦哥的模型,
這樣我們可以對程序進行細化的處理過程.
針對不同的標記進行處理:
1: /// <summary>
2: /// [Anonymous標記]驗證是否匿名訪問
3: /// </summary>
4: /// <param name="filterContext"></param>
5: /// <returns></returns>
6: public bool CheckAnonymous(ActionExecutingContext filterContext)
7: {
8: //驗證是否是匿名訪問的Action
9: object[] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AnonymousAttribute), true);
10: //是否是Anonymous
11: var Anonymous = attrsAnonymous.Length == 1;
12: return Anonymous;
13: }
14: /// <summary>
15: /// [LoginAllowView標記]驗證是否登錄就可以訪問(如果已經登陸,那么不對於標識了LoginAllowView的方法就不需要驗證了)
16: /// </summary>
17: /// <param name="filterContext"></param>
18: /// <returns></returns>
19: public bool CheckLoginAllowView(ActionExecutingContext filterContext)
20: {
21: //在這里允許一種情況,如果已經登陸,那么不對於標識了LoginAllowView的方法就不需要驗證了
22: object[] attrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(LoginAllowViewAttribute), true);
23: //是否是LoginAllowView
24: var ViewMethod = attrs.Length == 1;
25: return ViewMethod;
26: }
27:
28: /// <summary>
29: /// //權限判斷業務邏輯
30: /// </summary>
31: /// <param name="filterContext"></param>
32: /// <param name="isViewPage">是否是頁面</param>
33: /// <returns></returns>
34: protected virtual bool AuthorizeCore(ActionExecutingContext filterContext)
35: {
36:
37: if (filterContext.HttpContext == null)
38: {
39: throw new ArgumentNullException("httpContext");
40: }
41: //驗證當前Action是否是匿名訪問Action
42: if (CheckAnonymous(filterContext))
43: return true;
44: //未登錄驗證
45: if (SessionHelper.Get("UserID") == null)
46: {
47: return false;
48: }
49: //驗證當前Action是否是登錄就可以訪問的Action
50: if (CheckLoginAllowView(filterContext))
51: return true;
52:
53: //下面開始用戶權限驗證
54: var user = new UserService();
55: SysCurrentUser CurrentUser = new SysCurrentUser();
56: var controllerName = filterContext.RouteData.Values["controller"].ToString();
57: var actionName = filterContext.RouteData.Values["action"].ToString();
58: //如果是超級管理員,直接允許
59: if (CurrentUser.UserID == ConfigSettings.GetAdminUserID())
60: {
61: return true;
62: }
63: //如果擁有超級管理員的角色就默認全部允許
64: string AdminUserRoleID = ConfigSettings.GetAdminUserRoleID().ToString();
65: //檢查當前角色組有沒有超級角色
66: if (Tools.CheckStringHasValue(CurrentUser.UserRoles, ',', CurrentUser.UserRoles))
67: {
68: return true;
69: }
70:
71: //Action權限驗證
72: if (controllerName.ToLower() != "manage")//如果當前Action請求為具體的功能頁並且不是Manage中 Index頁和Welcome頁
73: {
74: //驗證
75: if (!user.RoleHasOperatePermission(CurrentUser.UserRoles, controllerName, actionName))//如果驗證該操作是否擁有權限
76: {
77: return false;
78: }
79: }
80: //管理頁面直接允許
81: return true;
82: }
可以看到我們僅僅這一個PermissionAttribute配合其他的Attribute就實現了權限的攔截控制.
當然,我們仍然需要配置那些東西?
過濾頁面.....
有一些頁面我們可以直接配置允許訪問的頁面:
1: var path = filterContext.HttpContext.Request.Path.ToLower();
2: //獲取當前配置保存起來的允許頁面
3: IList<string> allowPages = ConfigSettings.GetAllAllowPage();
4: foreach (string page in allowPages)
5: {
6: if (page.ToLower() == path)
7: {
8: IsIgnored = true;
9: break;
10: }
11: }
12: if (IsIgnored)
13: return;
我們在自定義配置文件中,可以定義允許訪問的頁面,比如:錯誤頁之類的,可能有些人覺得,不需要,但是這個配置是在程序發布以后,仍然能夠很輕松的進行配置,所以,預留一個配置頁面還是蠻有必要的.
同時我們也需要配置超級管理員身份和角色....
有人覺得,這些需要么?
我覺得是需要的,因為數據庫讀取出來的角色,根本沒辦法分辨超級管理員,只能數據庫動態配置,我們就預定義這樣的一個超級管理員角色的身份ID,讓這個超級管理角色擁有任何的功能和任何的權限,甚至讀取數據庫的時候,都是讀取的所有模塊權限,菜單權限.
這樣我們的配置工作就非常簡單了,特別是數據庫沒有大部分配置初試信息(比如菜單信息,角色信息,)的時候,我們不需要手動往數據庫添加數據,直接在程序中添加就可以了,也算是一個超級管理員身份.
接下來,我們的權限控制基本上就差不多了,現在我們可以安心的寫頁面了,權限神馬東西,跟咱關系就不大了,這樣,分工不是更爽,干別人的活才是最糾結的.......
接下來,我們可以繼續設計一些公共類來簡化我們日常的操作,對於公共類的設計,我覺得要拿出最大的熱情與態度,要知道這些是能夠真正節省我們時間的東西.
只有設計好它,我們以后才會更快更爽更輕松.
我們經常會遇到這樣的代碼:
1: /// <summary>
2: /// 保存資料業務
3: /// </summary>
4: /// <param name="context"></param>
5: public void SaveCompany(HttpContext context)
6: {
7: //用戶json數據讀取
8: company Info=new company ();
9: String CompanyStr = context.Request["Company"];
10: string id=context.Request["id"];
11: string pic = context.Request["pic"];
12: if (!Tools.IsValidInput(ref pic, true) || !Tools.IsValidInput(ref id, true))
13: {
14: return;
15: }
16: //圖片保存
17: //System.IO.StreamWriter sw = new System.IO.StreamWriter(context.Server.MapPath("tzt.txt"), true);
18: //sw.Write(CompanyStr);
19: //sw.Close();
20: //使用Newtonsoft.Json.dll組件解析json對象
21:
22: JObject o = JObject.Parse(CompanyStr);
23: Info.Username = (string)o.SelectToken("Username");
24: if (!new companyBLL().CheckExistUserName(Info.Username))
25: {
26: context.Response.Write(false);
27: return;
28: }
29: Info.Password = (string)o.SelectToken("Password");
30: Info.Name = (string)o.SelectToken("Name");
31: Info.Isrecommend = ((string)o.SelectToken("Isrecommend"))=="true" ? "1" : "0";
32: Info.Fac = (string)o.SelectToken("Fac");
33: Info.Representative = (string)o.SelectToken("Representative");
34: Info.Isshow = ((string)o.SelectToken("Isshow")) == "true" ? "1" : "0";
35: Info.State = ((string)o.SelectToken("State")) == "true" ? "1" : "0";
36: Info.State1 = ((string)o.SelectToken("State1")) == "true" ? "1" : "0";
37:
38: Info.Zipcode = (string)o.SelectToken("Zipcode");
39: Info.QQ = (string)o.SelectToken("QQ");
40: Info.Telephone = (string)o.SelectToken("Telephone");
41: Info.mobilephone = (string)o.SelectToken("mobilephone");
42: Info.Email = (string)o.SelectToken("Email");
43: Info.Address = (string)o.SelectToken("Address");
44: Info.Website = (string)o.SelectToken("Website");
45: Info.Award = (string)o.SelectToken("Award");
46: Info.Introduction = (string)o.SelectToken("Introduction");
47: Info.Picturepath = pic;
48:
49: if (!string.IsNullOrEmpty(id))
50: Info.Id = Convert.ToInt32(id);
51:
52: if (!Info.Id.HasValue)
53: {
54: Info.rank = 0;
55: Info.hit = 0;
56: //執行增加操作
57: new companyBLL().AddNew(Info);
58: SMTP smtp = new SMTP(Info.Email);
59: string webpath = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute("~/Default.aspx");
60: smtp.Activation(webpath, Info.Name);//發送激活郵件
61: }
62: else
63: {
64: new companyBLL().Update(Info);
65: }
66:
67:
68: }
這是我以前一個電子商務網站項目寫的,
我現在看,簡直到處都是毛病.......
首先,關於form讀取內容占了一大塊.......
可以想象,到處都是賦值語句,看到都吐血,再加上一些驗證非空,驗證函數等等,更是罪加一等的惡劣.
當然我們需要一步一步的解決這些問題,首先是剝離關於表單讀取的內容,封裝到一個類中,然后在類中進行驗證處理.
權限驗證中的樣例,
驗證也沒有做大塊的處理,不過大家可以注意,可以看到想當的簡潔,我們同時需要各種輔助類的設計,強制轉化的處理,非空驗證的處理,等等,我們直接設計成擴展方法就能達到上圖中ObjToIntNULL()的形式, 也不需要ConvertToInt32(xxxxx)的形式來轉化了,不是更簡潔么?
1: /* 作者: tianzh
2: * 創建時間: 2012/7/22 15:38:20
3: *
4: */
5: using System;
6: using System.Collections.Generic;
7: using System.Linq;
8: using System.Text;
9:
10: namespace TZHSWEET.Common
11: {
12: /// <summary>
13: /// 強制轉化輔助類(無異常拋出)
14: /// </summary>
15: public static class ConvertHelper
16: {
17: #region 強制轉化
18: /// <summary>
19: /// object轉化為Bool類型
20: /// </summary>
21: /// <param name="obj"></param>
22: /// <returns></returns>
23: public static bool ObjToBool(this object obj)
24: {
25: bool flag;
26: if (obj == null)
27: {
28: return false;
29: }
30: if (obj.Equals(DBNull.Value))
31: {
32: return false;
33: }
34: return (bool.TryParse(obj.ToString(), out flag) && flag);
35: }
36: /// <summary>
37: /// object強制轉化為DateTime類型(吃掉異常)
38: /// </summary>
39: /// <param name="obj"></param>
40: /// <returns></returns>
41: public static DateTime? ObjToDateNull(this object obj)
42: {
43: if (obj == null)
44: {
45: return null;
46: }
47: try
48: {
49: return new DateTime?(Convert.ToDateTime(obj));
50: }
51: catch (ArgumentNullException ex)
52: {
53: return null;
54: }
55: }
56: /// <summary>
57: /// int強制轉化
58: /// </summary>
59: /// <param name="obj"></param>
60: /// <returns></returns>
61: public static int ObjToInt(this object obj)
62: {
63: if (obj != null)
64: {
65: int num;
66: if (obj.Equals(DBNull.Value))
67: {
68: return 0;
69: }
70: if (int.TryParse(obj.ToString(), out num))
71: {
72: return num;
73: }
74: }
75: return 0;
76: }
77: /// <summary>
78: /// 強制轉化為long
79: /// </summary>
80: /// <param name="obj"></param>
81: /// <returns></returns>
82: public static long ObjToLong(this object obj)
83: {
84: if (obj != null)
85: {
86: long num;
87: if (obj.Equals(DBNull.Value))
88: {
89: return 0;
90: }
91: if (long.TryParse(obj.ToString(), out num))
92: {
93: return num;
94: }
95: }
96: return 0;
97: }
98: /// <summary>
99: /// 強制轉化可空int類型
100: /// </summary>
101: /// <param name="obj"></param>
102: /// <returns></returns>
103: public static int? ObjToIntNull(this object obj)
104: {
105: if (obj == null)
106: {
107: return null;
108: }
109: if (obj.Equals(DBNull.Value))
110: {
111: return null;
112: }
113: return new int?(ObjToInt(obj));
114: }
115: /// <summary>
116: /// 強制轉化為string
117: /// </summary>
118: /// <param name="obj"></param>
119: /// <returns></returns>
120: public static string ObjToStr(this object obj)
121: {
122: if (obj == null)
123: {
124: return "";
125: }
126: if (obj.Equals(DBNull.Value))
127: {
128: return "";
129: }
130: return Convert.ToString(obj);
131: }
132: /// <summary>
133: /// Decimal轉化
134: /// </summary>
135: /// <param name="obj"></param>
136: /// <returns></returns>
137: public static decimal ObjToDecimal(this object obj)
138: {
139: if (obj == null)
140: {
141: return 0M;
142: }
143: if (obj.Equals(DBNull.Value))
144: {
145: return 0M;
146: }
147: try
148: {
149: return Convert.ToDecimal(obj);
150: }
151: catch
152: {
153: return 0M;
154: }
155: }
156: /// <summary>
157: /// Decimal可空類型轉化
158: /// </summary>
159: /// <param name="obj"></param>
160: /// <returns></returns>
161: public static decimal? ObjToDecimalNull(this object obj)
162: {
163: if (obj == null)
164: {
165: return null;
166: }
167: if (obj.Equals(DBNull.Value))
168: {
169: return null;
170: }
171: return new decimal?(ObjToDecimal(obj));
172: }
173: #endregion
174:
175: }
176: }
一些好的常用的擴展方法同樣是一個相當大的改進,對於代碼,也更清晰,更簡潔,我們同時可以設計一些非空驗證等等.
1: #region 判斷對象是否為空
2: /// <summary>
3: /// 判斷對象是否為空,為空返回true
4: /// </summary>
5: /// <typeparam name="T">要驗證的對象的類型</typeparam>
6: /// <param name="data">要驗證的對象</param>
7: public static bool IsNullOrEmpty<T>(this T data)
8: {
9: //如果為null
10: if (data == null)
11: {
12: return true;
13: }
14:
15: //如果為""
16: if (data.GetType() == typeof(String))
17: {
18: if (string.IsNullOrEmpty(data.ToString().Trim())||data.ToString ()=="")
19: {
20: return true;
21: }
22: }
23:
24: //如果為DBNull
25: if (data.GetType() == typeof(DBNull))
26: {
27: return true;
28: }
29:
30: //不為空
31: return false;
32: }
33:
34: /// <summary>
35: /// 判斷對象是否為空,為空返回true
36: /// </summary>
37: /// <param name="data">要驗證的對象</param>
38: public static bool IsNullOrEmpty(this object data)
39: {
40: //如果為null
41: if (data == null)
42: {
43: return true;
44: }
45:
46: //如果為""
47: if (data.GetType() == typeof(String))
48: {
49: if (string.IsNullOrEmpty(data.ToString().Trim()))
50: {
51: return true;
52: }
53: }
54:
55: //如果為DBNull
56: if (data.GetType() == typeof(DBNull))
57: {
58: return true;
59: }
60:
61: //不為空
62: return false;
63: }
64: #endregion
這些東西其實真沒有太多難的東西,只是簡單的為了追求更簡潔更方便而已,磨刀不誤砍柴工就是這個道理,思考怎么偷懶才是程序員應該干的事情,不管多么忙都要停下來思考,想想怎么樣才能偷懶!
界面上,不太會美工,直接照搬的LigerUI界面!
目前進度已經完成的差不多了,接下來要陸續總結學到的經驗與技巧!
一步一步Asp.Net MVC系列_權限管理之權限控制
在權限管理中一個很重要的就是關於權限的攔截驗證問題,特別是我們在webform中的驗證,比純winform要更復雜,winform可以通過驗證把按鈕隱藏或者禁用的方式,但是在web中我們不能僅僅通過隱藏按鈕,不顯示菜單/按鈕之類的手段,因為客戶端的代碼都是透明的,如果我們不在服務端把好關,那么權限根本就無從談起,我們必須徹底的進行驗證,每一步動作都要進行驗證,客戶端的每一個ajax提交都要進行驗證,如果任何一個ajax 動作都做過驗證了,那么至少可以保證基本的安全性了.
在純webform中,我們通常怎么來進行權限控制呢?
一般情況下,設計基類然后,在基類寫好驗證方法,子類調用並驗證
我們來看看啟航動力的開源CMS怎么進行的驗證:
1: using System;
2: using System.Collections.Generic;
3: using System.Text;
4: using System.Web;
5: using System.Web.UI.WebControls;
6: using DTcms.Common;
7:
8: namespace DTcms.Web.UI
9: {
10: public class ManagePage : System.Web.UI.Page
11: {
12: protected internal Model.siteconfig siteConfig;
13:
14: public ManagePage()
15: {
16: this.Load += new EventHandler(ManagePage_Load);
17: siteConfig = new BLL.siteconfig().loadConfig(Utils.GetXmlMapPath("Configpath"));
18: }
19:
20: private void ManagePage_Load(object sender, EventArgs e)
21: {
22: //判斷管理員是否登錄
23: if (!IsAdminLogin())
24: {
25: Response.Write("<script>parent.location.href='" + siteConfig.webpath + siteConfig.webmanagepath + "/login.aspx'</script>");
26: Response.End();
27: }
28: }
29:
30: #region 管理員============================================
31: /// <summary>
32: /// 判斷管理員是否已經登錄(解決Session超時問題)
33: /// </summary>
34: public bool IsAdminLogin()
35: {
36: //如果Session為Null
37: if (Session[DTKeys.SESSION_ADMIN_INFO] != null)
38: {
39: return true;
40: }
41: else
42: {
43: //檢查Cookies
44: string adminname = Utils.GetCookie("AdminName", "DTcms"); //解密用戶名
45: string adminpwd = Utils.GetCookie("AdminPwd", "DTcms");
46: if (adminname != "" && adminpwd != "")
47: {
48: BLL.manager bll = new BLL.manager();
49: Model.manager model = bll.GetModel(adminname, adminpwd);
50: if (model != null)
51: {
52: Session[DTKeys.SESSION_ADMIN_INFO] = model;
53: return true;
54: }
55: }
56: }
57: return false;
58: }
59:
60: /// <summary>
61: /// 取得管理員信息
62: /// </summary>
63: public Model.manager GetAdminInfo()
64: {
65: if (IsAdminLogin())
66: {
67: Model.manager model = Session[DTKeys.SESSION_ADMIN_INFO] as Model.manager;
68: if (model != null)
69: {
70: return model;
71: }
72: }
73: return null;
74: }
75:
76: /// <summary>
77: /// 檢查管理員權限
78: /// </summary>
79: /// <param name="channel_id">頻道ID</param>
80: /// <param name="action_type">操作類型</param>
81: public void ChkAdminLevel(int channel_id, string action_type)
82: {
83: Model.manager model = GetAdminInfo();
84: BLL.manager_role bll = new BLL.manager_role();
85: bool result = bll.Exists(model.role_id, channel_id, action_type);
86: if (!result)
87: {
88: string msbox = "parent.f_errorTab(\"錯誤提示\", \"您沒有管理該頁面的權限,請勿嘗試非法進入!\")";
89: //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true); //修正BUG
90: Response.Write("<script type=\"text/javascript\">" + msbox + "</script>");
91: Response.End();
92: }
93: }
94:
95: /// <summary>
96: /// 檢查管理員權限
97: /// </summary>
98: /// <param name="channel_name">欄目名稱</param>
99: /// <param name="action_type">操作類型</param>
100: public void ChkAdminLevel(string channel_name, string action_type)
101: {
102: Model.manager model = GetAdminInfo();
103: BLL.manager_role bll = new BLL.manager_role();
104: bool result = bll.Exists(model.role_id, channel_name, action_type);
105: if (!result)
106: {
107: string msbox = "parent.f_errorTab(\"錯誤提示\", \"您沒有管理該頁面的權限,請勿嘗試非法進入!\")";
108: //ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox.ToString(), true); //修正BUG
109: Response.Write("<script type=\"text/javascript\">" + msbox + "</script>");
110: Response.End();
111: }
112: }
113:
114: /// <summary>
115: /// 檢查管理員權限
116: /// </summary>
117: /// <param name="channel_name">欄目名稱</param>
118: /// <param name="action_type">操作類型</param>
119: /// <returns>bool</returns>
120: public bool IsAdminLevel(string channel_name, string action_type)
121: {
122: Model.manager model = GetAdminInfo();
123: BLL.manager_role bll = new BLL.manager_role();
124: return bll.Exists(model.role_id, channel_name, action_type);
125: }
126:
127: #endregion
128:
129: #region 枚舉==============================================
130:
131: /// <summary>
132: /// 統一管理操作枚舉
133: /// </summary>
134: public enum ActionEnum
135: {
136: /// <summary>
137: /// 所有
138: /// </summary>
139: All,
140: /// <summary>
141: /// 查看
142: /// </summary>
143: View,
144: /// <summary>
145: /// 添加
146: /// </summary>
147: Add,
148: /// <summary>
149: /// 修改
150: /// </summary>
151: Edit,
152: /// <summary>
153: /// 刪除
154: /// </summary>
155: Delete
156: }
157:
158: /// <summary>
159: /// 屬性類型枚舉
160: /// </summary>
161: public enum AttributeEnum
162: {
163: /// <summary>
164: /// 輸入框
165: /// </summary>
166: Text,
167: /// <summary>
168: /// 下拉框
169: /// </summary>
170: Select,
171: /// <summary>
172: /// 單選框
173: /// </summary>
174: Radio,
175: /// <summary>
176: /// 復選框
177: /// </summary>
178: CheckBox
179: }
180: #endregion
181:
182: #region JS提示============================================
183:
184: /// <summary>
185: /// 添加編輯刪除提示
186: /// </summary>
187: /// <param name="msgtitle">提示文字</param>
188: /// <param name="url">返回地址</param>
189: /// <param name="msgcss">CSS樣式</param>
190: protected void JscriptMsg(string msgtitle, string url, string msgcss)
191: {
192: string msbox = "parent.jsprint(\"" + msgtitle + "\", \"" + url + "\", \"" + msgcss + "\")";
193: ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox, true);
194: }
195:
196: /// <summary>
197: /// 帶回傳函數的添加編輯刪除提示
198: /// </summary>
199: /// <param name="msgtitle">提示文字</param>
200: /// <param name="url">返回地址</param>
201: /// <param name="msgcss">CSS樣式</param>
202: /// <param name="callback">JS回調函數</param>
203: protected void JscriptMsg(string msgtitle, string url, string msgcss, string callback)
204: {
205: string msbox = "parent.jsprint(\"" + msgtitle + "\", \"" + url + "\", \"" + msgcss + "\", " + callback + ")";
206: ClientScript.RegisterClientScriptBlock(Page.GetType(), "JsPrint", msbox, true);
207: }
208: #endregion
209:
210: }
211: }
在子類校驗的時候繼承ManagePage的基類,然后就這樣校驗:
可以看到這種是通常的設計方案,基類定義,子類校驗,不過這個啟航動力CMS,他完全是在基類定義枚舉,控制僅僅停留在增刪改查,瀏覽,這些,不過CMS確實這一層就可以了,而且它的設計感覺不太好,因為到處都是驗證代碼,每一個增刪改查方法都有這些驗證的代碼,如果是我我就會用委托的方式綁定方法,在Page_Load里面驗證權限,委托綁定增刪改查方法,這樣,把驗證的過程大大節省,這些甚至可以設計成公共的方法,來實現.
我們今天講解的是MVC里面的權限驗證,MVC天然的Controller,Action,讓我們的權限控制更加容易,而且更簡潔,更清晰.
首先,先來看看,MVC里面的一個小特色設計:
這個是一個普通的model,在mvc示例項目中,他采用Attribute方式來驗證,我當時看到的時候感覺耳目一新,以前看<<CLR VIA C#>> Attribute的時候,當時還在想這些東西可以干什么??僅僅是元數據描述??后來才發現,這東西太強大了,控件的設計,反射,ORM,無處不在,Attributes也讓代碼更加優雅.
我們可以在mvc中定義各種Attribute來讓我們的代碼更優雅,而且讓權限更簡潔,比如我們經常會設計,
xxxx頁面登錄后才能訪問,
xxxxx頁面匿名用戶也能訪問,
xxxx頁面必須經過權限驗證才能訪問,
xxxxx各種類型
我們可以定義各種Attribute來描述它,這種描述也讓權限控制更加優雅.
比如:我們有幾個登錄頁面,錯誤顯示頁面等等,這些頁面,可以單獨設計一個Attribute標記來標識.
我們可以定義各種各樣的標記來描述權限控制,
可以看到我們在這里只需要標記匿名標記,然后在權限攔截中進行處理,如果存在Anonymous標記就默認允許.
這里在公共基類中設計驗證標記,子類繼承,這樣,我們就可以達到子類全部都需要驗證處理..
接下來,我們就看看,我們的權限攔截如何處理的.
我們的權限攔截是通過繼承ActionFilterAttribute,自定義攔截器,這里參考傳說中弦哥的思路.
簡單地說,ActionFilter就是Action過濾器,任何一個請求都像篩子一樣的過濾.所以,這個Filter就是篩子,我們需要的就是在這個篩子上做權限控制,這樣我們的權限不就容易得多了么?
這時候,我想到了以前的一張asp.net生命周期,一個請求過來同樣經過了HttpModule等一層層,在那里做權限攔截,可能效果更好,如果webform設計,可能要嘗試一下看看能不能這么做.
1: /// <summary>
2: /// 權限攔截
3: /// </summary>
4: [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)]
5: public class PermissionFilterAttribute : ActionFilterAttribute
6: {
7: /// <summary>
8: /// 權限攔截
9: /// </summary>
10: /// <param name="filterContext"></param>
11: public override void OnActionExecuting(ActionExecutingContext filterContext)
12: {
13: //權限攔截是否忽略
14: bool IsIgnored = false;
15: if (filterContext == null)
16: {
17: throw new ArgumentNullException("filterContext");
18: }
19: var path = filterContext.HttpContext.Request.Path.ToLower();
20: //獲取當前配置保存起來的允許頁面
21: IList<string> allowPages = ConfigSettings.GetAllAllowPage();
22: foreach (string page in allowPages)
23: {
24: if (page.ToLower() == path)
25: {
26: IsIgnored = true;
27: break;
28: }
29: }
30: if (IsIgnored)
31: return;
32: //接下來進行權限攔截與驗證
33: object[] attrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(ViewPageAttribute), true);
34: var isViewPage = attrs.Length == 1;//當前Action請求是否為具體的功能頁
35:
36: if (this.AuthorizeCore(filterContext) == false)//根據驗證判斷進行處理
37: {
38: //注:如果未登錄直接在URL輸入功能權限地址提示不是很友好;如果登錄后輸入未維護的功能權限地址,那么也可以訪問,這個可能會有安全問題
39: if (isViewPage == true)
40: {
41: //跳轉到登錄頁面
42: filterContext.RequestContext.HttpContext.Response.Redirect("~/Admin/Manage/UserLogin");
43: }
44: else
45: {
46:
47: //跳轉到登錄頁面
48: filterContext.RequestContext.HttpContext.Response.Redirect("~/Admin/Manage/Error");
49: }
50: }
51: }
這個Attribute直接借鑒的弦哥的模型,
這樣我們可以對程序進行細化的處理過程.
針對不同的標記進行處理:
1: /// <summary>
2: /// [Anonymous標記]驗證是否匿名訪問
3: /// </summary>
4: /// <param name="filterContext"></param>
5: /// <returns></returns>
6: public bool CheckAnonymous(ActionExecutingContext filterContext)
7: {
8: //驗證是否是匿名訪問的Action
9: object[] attrsAnonymous = filterContext.ActionDescriptor.GetCustomAttributes(typeof(AnonymousAttribute), true);
10: //是否是Anonymous
11: var Anonymous = attrsAnonymous.Length == 1;
12: return Anonymous;
13: }
14: /// <summary>
15: /// [LoginAllowView標記]驗證是否登錄就可以訪問(如果已經登陸,那么不對於標識了LoginAllowView的方法就不需要驗證了)
16: /// </summary>
17: /// <param name="filterContext"></param>
18: /// <returns></returns>
19: public bool CheckLoginAllowView(ActionExecutingContext filterContext)
20: {
21: //在這里允許一種情況,如果已經登陸,那么不對於標識了LoginAllowView的方法就不需要驗證了
22: object[] attrs = filterContext.ActionDescriptor.GetCustomAttributes(typeof(LoginAllowViewAttribute), true);
23: //是否是LoginAllowView
24: var ViewMethod = attrs.Length == 1;
25: return ViewMethod;
26: }
27:
28: /// <summary>
29: /// //權限判斷業務邏輯
30: /// </summary>
31: /// <param name="filterContext"></param>
32: /// <param name="isViewPage">是否是頁面</param>
33: /// <returns></returns>
34: protected virtual bool AuthorizeCore(ActionExecutingContext filterContext)
35: {
36:
37: if (filterContext.HttpContext == null)
38: {
39: throw new ArgumentNullException("httpContext");
40: }
41: //驗證當前Action是否是匿名訪問Action
42: if (CheckAnonymous(filterContext))
43: return true;
44: //未登錄驗證
45: if (SessionHelper.Get("UserID") == null)
46: {
47: return false;
48: }
49: //驗證當前Action是否是登錄就可以訪問的Action
50: if (CheckLoginAllowView(filterContext))
51: return true;
52:
53: //下面開始用戶權限驗證
54: var user = new UserService();
55: SysCurrentUser CurrentUser = new SysCurrentUser();
56: var controllerName = filterContext.RouteData.Values["controller"].ToString();
57: var actionName = filterContext.RouteData.Values["action"].ToString();
58: //如果是超級管理員,直接允許
59: if (CurrentUser.UserID == ConfigSettings.GetAdminUserID())
60: {
61: return true;
62: }
63: //如果擁有超級管理員的角色就默認全部允許
64: string AdminUserRoleID = ConfigSettings.GetAdminUserRoleID().ToString();
65: //檢查當前角色組有沒有超級角色
66: if (Tools.CheckStringHasValue(CurrentUser.UserRoles, ',', CurrentUser.UserRoles))
67: {
68: return true;
69: }
70:
71: //Action權限驗證
72: if (controllerName.ToLower() != "manage")//如果當前Action請求為具體的功能頁並且不是Manage中 Index頁和Welcome頁
73: {
74: //驗證
75: if (!user.RoleHasOperatePermission(CurrentUser.UserRoles, controllerName, actionName))//如果驗證該操作是否擁有權限
76: {
77: return false;
78: }
79: }
80: //管理頁面直接允許
81: return true;
82: }
可以看到我們僅僅這一個PermissionAttribute配合其他的Attribute就實現了權限的攔截控制.
當然,我們仍然需要配置那些東西?
過濾頁面.....
有一些頁面我們可以直接配置允許訪問的頁面:
1: var path = filterContext.HttpContext.Request.Path.ToLower();
2: //獲取當前配置保存起來的允許頁面
3: IList<string> allowPages = ConfigSettings.GetAllAllowPage();
4: foreach (string page in allowPages)
5: {
6: if (page.ToLower() == path)
7: {
8: IsIgnored = true;
9: break;
10: }
11: }
12: if (IsIgnored)
13: return;
我們在自定義配置文件中,可以定義允許訪問的頁面,比如:錯誤頁之類的,可能有些人覺得,不需要,但是這個配置是在程序發布以后,仍然能夠很輕松的進行配置,所以,預留一個配置頁面還是蠻有必要的.
同時我們也需要配置超級管理員身份和角色....
有人覺得,這些需要么?
我覺得是需要的,因為數據庫讀取出來的角色,根本沒辦法分辨超級管理員,只能數據庫動態配置,我們就預定義這樣的一個超級管理員角色的身份ID,讓這個超級管理角色擁有任何的功能和任何的權限,甚至讀取數據庫的時候,都是讀取的所有模塊權限,菜單權限.
這樣我們的配置工作就非常簡單了,特別是數據庫沒有大部分配置初試信息(比如菜單信息,角色信息,)的時候,我們不需要手動往數據庫添加數據,直接在程序中添加就可以了,也算是一個超級管理員身份.
接下來,我們的權限控制基本上就差不多了,現在我們可以安心的寫頁面了,權限神馬東西,跟咱關系就不大了,這樣,分工不是更爽,干別人的活才是最糾結的.......
接下來,我們可以繼續設計一些公共類來簡化我們日常的操作,對於公共類的設計,我覺得要拿出最大的熱情與態度,要知道這些是能夠真正節省我們時間的東西.
只有設計好它,我們以后才會更快更爽更輕松.
我們經常會遇到這樣的代碼:
1: /// <summary>
2: /// 保存資料業務
3: /// </summary>
4: /// <param name="context"></param>
5: public void SaveCompany(HttpContext context)
6: {
7: //用戶json數據讀取
8: company Info=new company ();
9: String CompanyStr = context.Request["Company"];
10: string id=context.Request["id"];
11: string pic = context.Request["pic"];
12: if (!Tools.IsValidInput(ref pic, true) || !Tools.IsValidInput(ref id, true))
13: {
14: return;
15: }
16: //圖片保存
17: //System.IO.StreamWriter sw = new System.IO.StreamWriter(context.Server.MapPath("tzt.txt"), true);
18: //sw.Write(CompanyStr);
19: //sw.Close();
20: //使用Newtonsoft.Json.dll組件解析json對象
21:
22: JObject o = JObject.Parse(CompanyStr);
23: Info.Username = (string)o.SelectToken("Username");
24: if (!new companyBLL().CheckExistUserName(Info.Username))
25: {
26: context.Response.Write(false);
27: return;
28: }
29: Info.Password = (string)o.SelectToken("Password");
30: Info.Name = (string)o.SelectToken("Name");
31: Info.Isrecommend = ((string)o.SelectToken("Isrecommend"))=="true" ? "1" : "0";
32: Info.Fac = (string)o.SelectToken("Fac");
33: Info.Representative = (string)o.SelectToken("Representative");
34: Info.Isshow = ((string)o.SelectToken("Isshow")) == "true" ? "1" : "0";
35: Info.State = ((string)o.SelectToken("State")) == "true" ? "1" : "0";
36: Info.State1 = ((string)o.SelectToken("State1")) == "true" ? "1" : "0";
37:
38: Info.Zipcode = (string)o.SelectToken("Zipcode");
39: Info.QQ = (string)o.SelectToken("QQ");
40: Info.Telephone = (string)o.SelectToken("Telephone");
41: Info.mobilephone = (string)o.SelectToken("mobilephone");
42: Info.Email = (string)o.SelectToken("Email");
43: Info.Address = (string)o.SelectToken("Address");
44: Info.Website = (string)o.SelectToken("Website");
45: Info.Award = (string)o.SelectToken("Award");
46: Info.Introduction = (string)o.SelectToken("Introduction");
47: Info.Picturepath = pic;
48:
49: if (!string.IsNullOrEmpty(id))
50: Info.Id = Convert.ToInt32(id);
51:
52: if (!Info.Id.HasValue)
53: {
54: Info.rank = 0;
55: Info.hit = 0;
56: //執行增加操作
57: new companyBLL().AddNew(Info);
58: SMTP smtp = new SMTP(Info.Email);
59: string webpath = context.Request.Url.Scheme + "://" + context.Request.Url.Authority + System.Web.VirtualPathUtility.ToAbsolute("~/Default.aspx");
60: smtp.Activation(webpath, Info.Name);//發送激活郵件
61: }
62: else
63: {
64: new companyBLL().Update(Info);
65: }
66:
67:
68: }
這是我以前一個電子商務網站項目寫的,
我現在看,簡直到處都是毛病.......
首先,關於form讀取內容占了一大塊.......
可以想象,到處都是賦值語句,看到都吐血,再加上一些驗證非空,驗證函數等等,更是罪加一等的惡劣.
當然我們需要一步一步的解決這些問題,首先是剝離關於表單讀取的內容,封裝到一個類中,然后在類中進行驗證處理.
權限驗證中的樣例,
驗證也沒有做大塊的處理,不過大家可以注意,可以看到想當的簡潔,我們同時需要各種輔助類的設計,強制轉化的處理,非空驗證的處理,等等,我們直接設計成擴展方法就能達到上圖中ObjToIntNULL()的形式, 也不需要ConvertToInt32(xxxxx)的形式來轉化了,不是更簡潔么?
1: /* 作者: tianzh
2: * 創建時間: 2012/7/22 15:38:20
3: *
4: */
5: using System;
6: using System.Collections.Generic;
7: using System.Linq;
8: using System.Text;
9:
10: namespace TZHSWEET.Common
11: {
12: /// <summary>
13: /// 強制轉化輔助類(無異常拋出)
14: /// </summary>
15: public static class ConvertHelper
16: {
17: #region 強制轉化
18: /// <summary>
19: /// object轉化為Bool類型
20: /// </summary>
21: /// <param name="obj"></param>
22: /// <returns></returns>
23: public static bool ObjToBool(this object obj)
24: {
25: bool flag;
26: if (obj == null)
27: {
28: return false;
29: }
30: if (obj.Equals(DBNull.Value))
31: {
32: return false;
33: }
34: return (bool.TryParse(obj.ToString(), out flag) && flag);
35: }
36: /// <summary>
37: /// object強制轉化為DateTime類型(吃掉異常)
38: /// </summary>
39: /// <param name="obj"></param>
40: /// <returns></returns>
41: public static DateTime? ObjToDateNull(this object obj)
42: {
43: if (obj == null)
44: {
45: return null;
46: }
47: try
48: {
49: return new DateTime?(Convert.ToDateTime(obj));
50: }
51: catch (ArgumentNullException ex)
52: {
53: return null;
54: }
55: }
56: /// <summary>
57: /// int強制轉化
58: /// </summary>
59: /// <param name="obj"></param>
60: /// <returns></returns>
61: public static int ObjToInt(this object obj)
62: {
63: if (obj != null)
64: {
65: int num;
66: if (obj.Equals(DBNull.Value))
67: {
68: return 0;
69: }
70: if (int.TryParse(obj.ToString(), out num))
71: {
72: return num;
73: }
74: }
75: return 0;
76: }
77: /// <summary>
78: /// 強制轉化為long
79: /// </summary>
80: /// <param name="obj"></param>
81: /// <returns></returns>
82: public static long ObjToLong(this object obj)
83: {
84: if (obj != null)
85: {
86: long num;
87: if (obj.Equals(DBNull.Value))
88: {
89: return 0;
90: }
91: if (long.TryParse(obj.ToString(), out num))
92: {
93: return num;
94: }
95: }
96: return 0;
97: }
98: /// <summary>
99: /// 強制轉化可空int類型
100: /// </summary>
101: /// <param name="obj"></param>
102: /// <returns></returns>
103: public static int? ObjToIntNull(this object obj)
104: {
105: if (obj == null)
106: {
107: return null;
108: }
109: if (obj.Equals(DBNull.Value))
110: {
111: return null;
112: }
113: return new int?(ObjToInt(obj));
114: }
115: /// <summary>
116: /// 強制轉化為string
117: /// </summary>
118: /// <param name="obj"></param>
119: /// <returns></returns>
120: public static string ObjToStr(this object obj)
121: {
122: if (obj == null)
123: {
124: return "";
125: }
126: if (obj.Equals(DBNull.Value))
127: {
128: return "";
129: }
130: return Convert.ToString(obj);
131: }
132: /// <summary>
133: /// Decimal轉化
134: /// </summary>
135: /// <param name="obj"></param>
136: /// <returns></returns>
137: public static decimal ObjToDecimal(this object obj)
138: {
139: if (obj == null)
140: {
141: return 0M;
142: }
143: if (obj.Equals(DBNull.Value))
144: {
145: return 0M;
146: }
147: try
148: {
149: return Convert.ToDecimal(obj);
150: }
151: catch
152: {
153: return 0M;
154: }
155: }
156: /// <summary>
157: /// Decimal可空類型轉化
158: /// </summary>
159: /// <param name="obj"></param>
160: /// <returns></returns>
161: public static decimal? ObjToDecimalNull(this object obj)
162: {
163: if (obj == null)
164: {
165: return null;
166: }
167: if (obj.Equals(DBNull.Value))
168: {
169: return null;
170: }
171: return new decimal?(ObjToDecimal(obj));
172: }
173: #endregion
174:
175: }
176: }
一些好的常用的擴展方法同樣是一個相當大的改進,對於代碼,也更清晰,更簡潔,我們同時可以設計一些非空驗證等等.
1: #region 判斷對象是否為空
2: /// <summary>
3: /// 判斷對象是否為空,為空返回true
4: /// </summary>
5: /// <typeparam name="T">要驗證的對象的類型</typeparam>
6: /// <param name="data">要驗證的對象</param>
7: public static bool IsNullOrEmpty<T>(this T data)
8: {
9: //如果為null
10: if (data == null)
11: {
12: return true;
13: }
14:
15: //如果為""
16: if (data.GetType() == typeof(String))
17: {
18: if (string.IsNullOrEmpty(data.ToString().Trim())||data.ToString ()=="")
19: {
20: return true;
21: }
22: }
23:
24: //如果為DBNull
25: if (data.GetType() == typeof(DBNull))
26: {
27: return true;
28: }
29:
30: //不為空
31: return false;
32: }
33:
34: /// <summary>
35: /// 判斷對象是否為空,為空返回true
36: /// </summary>
37: /// <param name="data">要驗證的對象</param>
38: public static bool IsNullOrEmpty(this object data)
39: {
40: //如果為null
41: if (data == null)
42: {
43: return true;
44: }
45:
46: //如果為""
47: if (data.GetType() == typeof(String))
48: {
49: if (string.IsNullOrEmpty(data.ToString().Trim()))
50: {
51: return true;
52: }
53: }
54:
55: //如果為DBNull
56: if (data.GetType() == typeof(DBNull))
57: {
58: return true;
59: }
60:
61: //不為空
62: return false;
63: }
64: #endregion
這些東西其實真沒有太多難的東西,只是簡單的為了追求更簡潔更方便而已,磨刀不誤砍柴工就是這個道理,思考怎么偷懶才是程序員應該干的事情,不管多么忙都要停下來思考,想想怎么樣才能偷懶!
界面上,不太會美工,直接照搬的LigerUI界面!
目前進度已經完成的差不多了,接下來要陸續總結學到的經驗與技巧!