為什么獲取的System.Web.HttpContext.Current值為null,HttpContext對象為null時如何獲取程序(站點)的根目錄


ASP.NET提供了靜態屬性System.Web.HttpContext.Current,因此獲取HttpContext對象就非常方便了。也正是因為這個原因,所以我們經常能見到直接訪問System.Web.HttpContext.Current的代碼:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 
 6 namespace Test_HttpContext.Current
 7 {
 8     public class Test
 9     {
10 
11         public Test()
12         {
13             string file =System.Web.HttpContext.Current.Request.MapPath("~/App_Data/1.xml");
14 
15             string text = System.IO.File.ReadAllText(file);
16 
17             //..........其它的操作
18         }
19 
20         // 或者在一些方法中直接使用HttpContext.Current
21         public void Test_1()
22         {
23             string url = System.Web.HttpContext.Current.Request.RawUrl;
24 
25             string username = System.Web.HttpContext.Current.Session["username"].ToString();
26 
27             string value = (string)System.Web.HttpContext.Current.Items["key"];
28         }
29 
30         // 甚至還設計成靜態屬性
31         public static string Test_2
32         {
33             get
34             {
35                 return (string)System.Web.HttpContext.Current.Items["XXX"];
36             }
37         }
38 
39         /// <summary>
40         /// 獲取文件絕對路徑
41         /// </summary>
42         /// <param name="fileName">文件名稱</param>
43         /// <returns></returns>
44         public string Test_3(string fileName)
45         {
46             return System.Web.HttpContext.Current.Server.MapPath("~/Log" + fileName);
47         }
48 
49     }
50 }

上面的這些代碼這樣寫真的沒有問題嗎?
答案是否定的

請看下面的驗證:

我們先來看看HttpContext到底存儲在哪里:

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using System.Web.UI;
 6 using System.Web.UI.WebControls;
 7 
 8 namespace Test_HttpContext.Current
 9 {
10     public partial class WebForm1 : System.Web.UI.Page
11     {
12         protected void Page_Load(object sender, EventArgs e)
13         {
14 
15             HttpContext context1 = System.Web.HttpContext.Current;
16 
17             HttpContext context2 = System.Runtime.Remoting.Messaging.CallContext.HostContext as HttpContext;  //當前(請求)線程上下文
18 
19             bool isEqual = object.ReferenceEquals(context1, context2);
20 
21             Response.Write("context1與context2是否相同的實例:" + isEqual);
22         }
23     }
24 }

上面的代碼運行的結果是true:

從這段代碼來看,HttpContext其實是保存在System.Runtime.Remoting.Messaging.CallContext.HostContext這個屬性中, System.Runtime.Remoting.Messaging.CallContext.HostContext在MSDN的解釋是 獲取或設置與當前線程相關聯的主機上下文

我們在一個ASP.NET程序中,為什么可以到處訪問HttpContext.Current呢?
因為ASP.NET會為每個請求分配一個線程(也是當前線程),這個線程會執行我們的代碼來生成響應結果, 即使我們的代碼散落在不同的地方(類庫),線程仍然會執行它們, 所以我們可以在任何地方訪問System.Web.HttpContext.Current獲取到與當前請求相關的HttpContext對象, 這些代碼是由同一個線程來執行,所以得到的HttpContext引用也就是我們期待的那個與請求相關的對象。

當前線程是什么意思?
我的理解是:
1. 當前線程是指與當前請求相關的線程。
2. 在ASP.NET程序中,有些線程並非總是與請求相關。

雖然在ASP.NET程序中,幾乎所有的線程都應該是為響應請求而運行的,但是還有一些線程卻不是為了響應請求而(產生)運行的,

例如:
1. 定時器的回調。
2. Cache的移除通知。
3. APM模式下異步完成回調。
4. 主動創建線程或者將任務交給線程池來執行。

5.異步任務Task

至於什么APM網上資料很多,這里我就不說明了

在這些情況下使用System.Web.HttpContext.Current獲取HttpContext對象得到的結果都是null,因為處理他們的線程不是當前線程(為處理請求產生線程)

說的這里我們再回頭看看本文開始寫的(部分)代碼:

1          /// <summary>
2         /// 獲取文件絕對路徑
3         /// </summary>
4         /// <param name="fileName">文件名稱</param>
5         /// <returns></returns>
6         public string Test_3(string fileName)
7         {
8             return System.Web.HttpContext.Current.Server.MapPath("~/Log" + fileName);
9         }

如果這段代碼在那5種情況下運行,都會拋空指針異常,因為System.Web.HttpContext.Current得到是null。

為什么會得到null呢?

因為運行這段代碼線程不是處理當前請求的當前線程

為什么其他地方得到又不是null呢?

因為ASP.NET程序在調用您的代碼前,已經將HttpContext對象設置到前面所說的System.Runtime.Remoting.Messaging.CallContext.HostContext屬性中。

HttpApplication有個內部方法OnThreadEnter(),ASP.NET在調用外部代碼前會調用這個方法來切換HttpContext, 例如:每當執行管線的事件處理器之前,或者同步上下文(AspNetSynchronizationContext)執行回調時。 切換線程的CallContext.HostContext屬性之后,我們的代碼就可以訪問到HttpContext引用。 注意:HttpContext的引用其實是保存在HttpApplication對象中。

 

這種情況下該如何獲取文件的絕對路徑呢?

我們可以訪問System.Web.HttpRuntime.AppDomainAppPath獲取程序的根路徑,然后再拼接文件的相對路徑即可

上面的代碼得到的HttpContext對象是null,再調用MapPath來獲取站點根目錄,就必死無疑了!

所以在此建議大家在獲取程序(站點)的根目錄時盡量使用System.Web.HttpRuntime.AppDomainAppPath進行獲取站點的根目錄

 

那么在異步調用調用時訪問HttpContext對象呢?

前面我還提到在APM模式下的異步完成回調時,訪問HttpContext.Current也會返回null,那么此時該怎么辦呢?
1. 在類型中添加一個字段來保存HttpContext的引用(異步開始前)。
2. 將HttpContext賦值給BeginXXX方法的最后一個參數(object state)

建議優先選擇第二種方法,因為可以防止以后他人維護時數據成員被意外使用。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM