一. 基本認識
1. 簡介:HttpContext用於保持單個用戶、單個請求的數據,並且數據只在該請求期間保持;
也可以用於保持需要在不同的HttpModules和HttpHandlers之間傳遞的值;
也可以用於保持某個完整請求的相應信息。
2. 五大核心對象包括:Response、Request、Application、Server、Session
3. 源碼分析:在MVC框架中HttpContext對象來源於一個HttpContextBase類的一個實例,該類中包括以下幾個重要屬性:(非MVC框架中有一個單獨HttpContext類)
①:HttpRequestBase Request
②:HttpResponseBase Response
③:HttpApplicationStateBase Application
④:HttpServerUtilityBase Server
⑤:HttpSessionStateBase Session
即所謂的五大核心對象,所以在MVC框架中通常可以這么用: HttpContext.Request、HttpContext.Response、HttpContext.Application、 HttpContext.Server、HttpContext.Session .
但我們也經常看到省略HttpContext對象,直接:Request、Response、Server、Session 來使用,隨意選中其中一個,點擊F12查看源碼可知,Controller類中也包含以下四個屬性:
①:HttpRequestBase Request
②:HttpResponseBase Response
③:HttpServerUtilityBase Server
④:HttpSessionStateBase Session
二. 逐個分析
1. Request對象
1. Request:接收處理客戶端發送過來的請求 (已完成)
①:HttpMethod屬性:獲取客戶端發送請求的類型
②:RawUrl屬性:獲取當前請求完整的URL
③:UrlReferrer屬性:獲取有關鏈接到當前 URL 的客戶端請求的 URL
④:Request["XX"]:獲取xx屬性值
2. 代碼測試
1 //1.測試Request對象 2 $("#btn1").click(function () { 3 $.ajax({ 4 type: "Post", 5 url: "TestRequest", 6 data: { 7 "name": "123" 8 }, 9 success: function (data) { 10 if (data.status == "ok") { 11 alert("測試通過"); 12 $("#s1").html(data.HttpMethod); 13 $("#s2").html(data.RawUrl); 14 $("#s3").html(data.UrlReferrer); 15 $("#s4").html(data.name); 16 } 17 if (data == "error") { 18 alert("測試未通過"); 19 } 20 } 21 }); 22 });
1 /// <summary> 2 /// 測試Request對象相關 3 /// </summary> 4 /// <returns></returns> 5 public ActionResult TestRequest() 6 { 7 var HttpMethod = Request.HttpMethod; 8 var RawUrl = Request.RawUrl; 9 var UrlReferrer = Request.UrlReferrer; 10 var name = Request["name"]; 11 var data = new 12 { 13 status="ok", 14 HttpMethod=HttpMethod, 15 RawUrl= RawUrl, 16 UrlReferrer= UrlReferrer, 17 name= name 18 }; 19 return Json(data); 20 }
測試結果:
2. Server對象
1. Server:一個輔助類 (已完成)
①.HtmlEncode方法:將html編碼轉換成對應的字符串
②.HtmlDecode方法:將字符串轉換成Html編碼
③.MapPath方法:獲取該地址對應的物理路徑
④.UrlEncode方法:將url編碼轉換成對應的字符串
⑤.UrlDecode方法:將對應的字符串轉換成Url格式的編碼
2. 代碼測試
1 //2.測試Server對象 2 $("#btn2").click(function () { 3 $.ajax({ 4 type: "Post", 5 url: "TestServer", 6 data: { 7 8 }, 9 success: function (data) { 10 if (data.status == "ok") { 11 alert("測試通過"); 12 $("#s11").html(data.HtmlEncode); 13 $("#s22").html(data.HtmlDecode); 14 $("#s33").html(data.MapPath); 15 $("#s44").html(data.UrlEncode); 16 $("#s55").html(data.UrlDecode); 17 } 18 if (data == "error") { 19 alert("測試未通過"); 20 } 21 } 22 }); 23 });
1 public ActionResult TestServer() 2 { 3 string HtmlEncode = Server.HtmlEncode("<p>積極向上</p>"); 4 string HtmlDecode = Server.HtmlDecode(HtmlEncode); 5 string MapPath = Server.MapPath("/home/index"); //只能做物理文件的映射 6 string UrlEncode = Server.UrlEncode("https://www.2345.com/"); 7 string UrlDecode = Server.UrlDecode(UrlEncode); 8 var data = new 9 { 10 status = "ok", 11 HtmlEncode = HtmlEncode, 12 HtmlDecode = HtmlDecode, 13 MapPath = MapPath, 14 UrlEncode = UrlEncode, 15 UrlDecode = UrlDecode 16 }; 17 return Json(data); 18 }
測試結果:
3. Application對象
1. Application: 用於在ASP.NET 應用程序內的多個會話和請求之間共享信息(案例:單點登錄)
①. Lock方法:鎖定對象的訪問
②. unLock方法:取消鎖定對象的訪問
③. Add和Set方法:向集合中添加一個新對象
④. Application[]和Get方法:獲取對象的值
⑤. Remove、RemoveAt、RemoveAll方法:用來刪除對象
補充一個單一對話: HttpContextBase context.Item[""] ; 是指是一個IDictionary類,有Add、Clear、Contains、Remove等方法(不做測試)
2. 測試:
①. 用不同的瀏覽器打開Index頁面,模擬多線程多個會話,發現name2的值是相同的。
4. Response對象
1. Response:用戶服務器端將信息返回給客戶端
①.ContentType屬性:獲取或設置當前響應的 HTTP MIME 類型 (非常重要!),下面補充幾個常用的類型(多用於下載)
a. Json格式: "application/json"
b. JavaScript格式:"application/x-javascript"
c. 普通文本格式:"text/plain"
d. Html格式:"text/html"
e. XML格式:"text/xml"
f. Excel格式:"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
g. gif圖片格式:"image/gif"
②.Redirect方法:用於站內跳轉或站點間的跳轉
③.Write方法:可以將字符、字符串、字節等寫入到Http響應輸出流 (MVC中的Content方法就是基於該方法進行擴展的)
④.WriteFile和TransmitFile方法:將指定文件寫入到Http相應輸出流 (MVC中的FileResult就是基於該方法封裝的,簡化了下載流程)
(原生的四種下載方法詳見:https://www.cnblogs.com/weixing/archive/2012/02/27/2369567.html (不做測試了))
2. 測試利用FileResult下載文件
1 /// <summary> 2 /// 測試下載文件 3 /// </summary> 4 /// <param name="type">1代表圖片 2代表Excel文件</param> 5 /// <returns></returns> 6 public ActionResult TestDownFile(string type) 7 { 8 if (type == "1") 9 { 10 //需要下載的文件在服務器上的物理路徑 11 string path = Server.MapPath("/Content/imageHandle/pipe1.jpg"); 12 //需要下載的文件下載后保存到本地后的名字 13 string fileName = DateTime.Now.ToString("yyyyMMddHHmmssffffff") + ".jpg"; 14 return File(path, "image/jpg", fileName); 15 } 16 if (type == "2") 17 { 18 string path = Server.MapPath("/Content/myExcel/text.xlsx"); 19 string fileName = DateTime.Now.ToString("yyyyMMddHHmmssffffff") + ".xlsx"; 20 return File(path, "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", fileName); 21 } 22 return Content(""); 23 } 24
1 //4. 測試下載文件 2 //4.1 下載圖片 3 $("#btn5").click(function () { 4 window.location.href = "TestDownFile?type=1"; 5 }); 6 //4.2 下載Excel 7 $("#btn6").click(function () { 8 window.location.href = "TestDownFile?type=2"; 9 });
結果:
5. Session對象
Session:存儲於服務器內存中key-value集合
(1).補充Cookie的概念:Cookie存儲於客戶端,通過瀏覽器的【Response Header】標頭下的Set-Cookie進行存儲,由瀏覽器進行建立、
保存、和到期刪除。Cookie默認的生命周期是瀏覽器的生命周期,瀏覽器關閉,cookie消失,當然也可以顯式的設置Cookie的到期時間,瀏覽器會根據這個到期
時間,將Cookie存放到客戶端硬盤的。
注意:Cookie在瀏覽器可以被看到,所以不適合存放敏感信息;Cookie能存儲的數據有限,最大4kb;Cookie是按照瀏覽器進行划分的。
(2).Http的請求流程:
①:客戶端第一次訪問,沒有Cookie,沒有Session,服務器端會生成一個SessionId和一個空的Value,保存到服務器內存上。
②:客戶端第一次返回,通過瀏覽器的【Response Header】標頭下的Set-Cookie進行輸出。
③:客戶端第二次訪問,會帶上Cookie進行訪問,且Cookie中存放着SessionId,服務器端通過SessionMoudle解析得到SessionId,獲取內存
中存放的數據,並且知道剛才你來過。
④:接下來在Session沒有過期和不關閉的瀏覽器的情況下,每次訪問服務器端,都會帶着同一個SessionId進行訪問。
圖解:
(3).深入剖析Session
①:同一個瀏覽器在不關閉的情況下(且服務器端Session沒有過期),無論訪問幾次服務器端,SessionId都是相同的,因為每次請求都會帶着Cookie中存儲的SessionId的。
②:關閉瀏覽器,再次請求服務器,因為【Request Header】表頭中,沒有提交剛才的SessionId,服務器會認為這是一個新的請求,服務器會分配給你一個新的
SessionId,這就解釋了原SessionId並沒有過期為什么服務器會重新分配給你一個SessionId的原因了。
③:同一個瀏覽器登錄狀態下,長時間沒有任何操作(Session默認過期時間為20分鍾),再次操作的時候,雖然Cookie中帶着SessionId進行
訪問服務器端,但是原Session已經被服務器端給清除了,只保留SessionId,原存放到Session中的內容均為null。
④:Session不能跨進程訪問,只能由當前會話的用戶訪問,因為SessionId是以Cookie的形式存放在訪問者的客戶端瀏覽器中。
⑤:在同一個會話中,Session是可以跨頁面進行全局使用的。
⑥:服務器上的Session保存的是一個SessionId和一個Value,而Value是一個集合,該集合中存放着用戶需要的信息。
(4).日常開發Session的局限性
在我們開發管理系統中,通常登錄后會把該用戶的權限放到Session中(如:放到Session["user"]中),這時候,在同一個瀏覽器中登錄admin賬戶,
會把a、b、c權限放到Session["user"]中,在不關閉該瀏覽器的情況下,登錄admin2賬戶,這時候同一個瀏覽器請求的【Request Header】表頭中
的cookie存放的SessionId是相同的,所以服務器端會認為是同一個會話,admin2賬戶的c,d權限會把原先的Session["user"]中的a、b、c權限覆蓋,
導致admin賬戶操作系統的時候,發現自己沒有 a、b、c權限,而是有c、d權限。
(如何解決這個問題呢?可以使用NoSQL來替代Session)
(5).Session的聲明和銷毀
①:從寫入開始,如果該頁面一直沒有操作,默認20分鍾,服務器會把session銷毀,但SessionId還是存在的。
②:手動通過Abandon的方式取消當前會話
③:刪除客戶端的Cookie,會導致服務器端重新分配給客戶端一個SessionId,其實服務器端的原Session並沒有消失
(6).總結:
客戶端向服務器發請求,如果請求頭中沒有帶SessionId,服務器會分配一個SessionId給客戶端,並存放在客戶端的Cookie中;
客戶端向服務器發送請求,如果cookie中有值,會帶着該值一起發送到服務器,如果沒有,則不帶。
(7).補充Session的幾個常用方法
①:Session[]:用於設置或讀取Session的值
②:Timeout屬性:設置Session過期時間
(補充通過配置文件的形式設置全局session過期:System.Web下添加 <sessionState mode="InProc" timeout="30"/> )
InProc表示進程內;timeout表示多少分鍾后Session失效。
③:SessionID屬性:每個會話的唯一標識符
④:Remove方法:從會話集合中刪除一項;Clear方法:從會話集合中刪除所有的鍵和值;Abandon方法:取消當前會話
MVC中的TempData就是基於Session實現的,但是TempData只能讀取一次
(8). 測試:
①:測試同一個瀏覽器,不關閉情況下SessionId相同,且同一個key的數據會被后面覆蓋
②:測試服務器端Session過期后,SessionId還存在,Session中的值均為Null(銷毀也是這個效果)
③:通過F12 瀏覽器驗證http流程,並查看【Response Header】和【Request Header】中什么時候又SessionId
④:手動刪除客戶端的Cookie,再次請求服務器,會重新分配新的SessionId
先貼出來幾塊代碼:
a:打開該頁面,顯示SessionId和name的值,如下圖:
b:分析該頁面打開的請求,如下圖:印證Http請求第一次請求和第一次返回的請求流程
c. 點擊按鈕多次,獲取SessionId和name的值,結果如下圖:SessionId和name的值都是相同的。(但刷新頁面,name值是變得,SessionID依舊不變)
d. 查看該按鈕對應的請求,如下圖:
e: 不關閉該瀏覽器的情況下,重新在一個新的選項卡中打開Index頁面,SessionId沒有變,name重新賦值了。
f:回到第一次打開的頁面,重新點擊測試按鈕,獲取SessionId和name值,SessionId沒有變,但是name值被第二次打開的頁面的name值覆蓋了。(印證了管理系統中權限覆蓋的問題)
g:點擊銷毀Session,然后重新點擊測試按鈕,查看結果,SessionId依舊存在,但是Session的value集合變成null了(等待服務器session自動過期也是這種效果)
!
- 作 者 : Yaopengfei(姚鵬飛)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 聲 明1 : 本人才疏學淺,用郭德綱的話說“我是一個小學生”,如有錯誤,歡迎討論,請勿謾罵^_^。
- 聲 明2 : 原創博客請在轉載時保留原文鏈接或在文章開頭加上本人博客地址,否則保留追究法律責任的權利。