IsPostBack深入探討(轉轉轉轉轉)
1 IsPostBack介紹
IsPostBack是Page類有一個bool型別的屬性,用來判斷針對當前Form的請求是第一次還是非第一次請求。當IsPostBack=true時表示非第一次請求,我們稱為PostBack,當IsPostBack=false時表示第一次請求。在asp.net框架內部有很多的場景需要判斷IsPostBack,比如LoadAllState等操作就需要在PostBack的時候進行。對於我們自己使用WebForm進行開發時,經常會在Page_Load中對IsPostBack進行判斷,因為第一次請求的時候會執行Page_Load,在非第一次請求的時候也會執行Page_Load。為什麼對同一個Form有多次請求呢?asp.net中引入了伺服器端事件,支援伺服器端事件的控制元件,會發出對當前Form的請求,這樣在很多情形下我們就需要區別是否是對這個Form的第一次請求。
2 IsPostBack結論
本人對.Net的原始碼中相關的處理進行的分析得到如下的結論:
結論① 對於使用Server.Transfer進行遷移時遷移到的頁面其IsPostBack=false。
結論② Post方式如果Request中沒有請求值,即Request.Form. =null則IsPostBack=false;Get方式如果Request中沒有請求值,即Request.QueryString =null則IsPostBack=false。
結論③ 如果QueryString或Form雖然有請求值,但是QueryString或Form中的Key沒有“__VIEWSTATE”和“__EVENTTARGET”和“__VIEWSTATEFIELDCOUNT”,並且沒有鍵為“null”,值以“__VIEWSTATE”開頭並且也沒有值為“__EVENTTARGET”的鍵值對,則IsPostBack=false。
結論④ 使用Response.Redirect方式向自畫面遷移時,此時IsPostBack=false。
結論⑤ 發生跨頁提交(CrossPagePostBack),當訪問PreviousPage屬性的時候,對於源Page,IsPostBack=true。
結論⑥ 發生跨頁提交(CrossPagePostBack)時目標頁面是IsPostBack=false
結論⑦ 使用Server.Execute遷移到的頁面其IsPostBack=false。
結論⑧ 在Page執行期間其對應的DLL被更新了並且Page的樹結構發生過變化,這種情況下請求時IsPostBack=false。
可以這樣來理解這些結論:一般情況判斷Request中如果沒有請求值則IsPostBack=false。如果有請求值但是不包括“__VIEWSTATE”等一些特殊的鍵或值,則IsPostBack=false(每次請求後.Net框架會將一些特殊的隱藏域“__VIEWSTATE”等返回給客戶端)。還有一些特殊的情形是上面的規則不能正確判斷的需要特殊處理的,這些情形包括Server.Transfer,Response.Redirect,CrossPagePostBack,Server.Execute,發生了頁面元素變化及重新編譯。
一般來說記住上面的結論就可以,如果您有興趣,或者懷疑請繼續看下面的IsPostBack推論過程。
3 IsPostBack推論過程
下面是根據.Net框架中的原始碼,來分析IsPostBack是如何判斷出來的。對於這些結論的推斷本人做了相關的試驗來證明推論的正確性,由於篇幅的原因沒有將這些試驗程式碼體現出來。另外不可能將全部的.Net框架的程式碼都體現出來,只是將相關的程式碼片段列出,說明推斷的依據。另外由於本人水平有限對.Net框架的程式碼理解還存在的不足的地方,請發現後進行指正,謝謝。
publicbool IsPostBack
{
get
{
if (this._requestValueCollection == null)
{
return false;
}
if (this._isCrossPagePostBack)
{
return true;
}
if (this_pageFlags[8])
{
return false;
}
return (
(
(this.Context.ServerExecuteDepth <= 0) ||
((this.Context.Handler != null) &&
(base.GetType() == this.Context.Handler.GetType()))
) && !this._fPageLayoutChanged
);
}
}
我們將每一個if判斷作為一個小節,作如下的分析。
3.1 this._requestValueCollection == null
if (this._requestValueCollection == null)
{
return false;
}
可以看出_requestValueCollection等於null時IsPostBack就等於false。
在Page.ProcessRequestMain(bool, bool)中有如下的程式碼:
if (this.PageAdapter != null)
{
this._requestValueCollection = this.PageAdapter.DeterminePostBackMode();
}
else
{
this._requestValueCollection = this.DeterminePostBackMode();
}
PageAdapter.DeterminePostBackMode最終還是呼叫了Page.DeterminePostBackMode,下面我們看Page.DeterminePostBackMode如何實現。
protected internal virtual NameValueCollection DeterminePostBackMode()
{
if (this.Context.Request == null)
{
return null;
}
if (this.Context.PreventPostback)
{
return null;
}
NameValueCollection collectionBasedOnMethod = this.GetCollectionBasedOnMethod(false);
if (collectionBasedOnMethod == null)
{
return null;
}
bool flag = false;
string[] values = collectionBasedOnMethod.GetValues((string) null);
if (values != null)
{
int length = values.Length;
for (int i = 0; i < length; i++)
{
if (values[i].StartsWith("__VIEWSTATE", StringComparison.Ordinal) ||
(values[i] == "__EVENTTARGET"))
{
flag = true;
break;
}
}
}
if (((collectionBasedOnMethod["__VIEWSTATE"] == null) && (collectionBasedOnMethod["__VIEWSTATEFIELDCOUNT"] == null)) && ((collectionBasedOnMethod["__EVENTTARGET"] == null) && !flag))
{
return null;
}
if (this.Request.QueryStringText.IndexOf(
HttpResponse.RedirectQueryStringAssignment, StringComparison.Ordinal) != -1)
{
collectionBasedOnMethod = null;
}
return collectionBasedOnMethod;
}
這個函式中返回null就意味者IsPostBack=false,將上面函式中每個返回為null的地方作如下的分析。
3.1.1 this.Context.Request == null
if (this.Context.Request == null)
{
return null;
}
this.Context.Request == null應該只有在異常的情況下會發生,正常情況下會在HttpRuntime.ProcessRequestInternal中建立HttpContext及HttpRequest物件。
3.1.2 this.Context.PreventPostback
if (this.Context.PreventPostback)
{
return null;
}
在HttpServerUtility.Transfer中會使用PreventPostback,其程式碼如下:
public void Transfer(string path)
{
bool preventPostback = this._context.PreventPostback;
this._context.PreventPostback = true;
this.Transfer(path, true);
this._context.PreventPostback = preventPostback;
}
在呼叫Server.Transfer進行畫面遷移時設定Context.PreventPostback=ture。此處得出結論①:對於使用Server.Transfer進行遷移時遷移到的頁面其IsPostBack=false。
3.1.3 collectionBasedOnMethod == null
NameValueCollection collectionBasedOnMethod = this.GetCollectionBasedOnMethod(false);
if (collectionBasedOnMethod == null)
{
return null;
}
呼叫了Page.GetCollectionBasedOnMethod後其返回值進行判斷。如果其返回值為null則IsPostBack為false。Page.GetCollectionBasedOnMethod的定義如下:
internal NameValueCollection GetCollectionBasedOnMethod(bool dontReturnNull)
{
if (this._request.HttpVerb == HttpVerb.POST)
{
if (!dontReturnNull && !this._request.HasForm)
{
return null;
}
return this._request.Form;
}
if (!dontReturnNull && !this._request.HasQueryString)
{
return null;
}
return this._request.QueryString;
}
從上面的程式碼可以看出返回值為null的情形是_request.HasForm=null或_request.HasQueryString=null。此處得出結論②:Post方式如果Request中沒有請求值,即Request.Form. =null則IsPostBack=false;Get方式如果Request中沒有請求值,即Request.QueryString =null則IsPostBack=false。
3.1.4 ((collectionBasedOnMethod["__VIEWSTATE"] == null) && (collectionBasedOnMethod["__VIEWSTATEFIELDCOUNT"] == null)) && ((collectionBasedOnMethod["__EVENTTARGET"] == null) && !flag)
bool flag = false;
string[] values = collectionBasedOnMethod.GetValues((string) null);
if (values != null)
{
int length = values.Length;
for (int i = 0; i < length; i++)
{
if (values[i].StartsWith("__VIEWSTATE", StringComparison.Ordinal) ||
(values[i] == "__EVENTTARGET"))
{
flag = true;
break;
}
}
}
上面這段程式碼的意思是判斷請求的鍵值對中是否存在沒有鍵,其值以“__VIEWSTATE”開頭或者其值為“__EVENTTARGET”。例如如下的Get請求方式會使得flag=true。
…/defalt.aspx?__VIEWSTATE
…/defalt.aspx?__EVENTTARGET
對於Get方式“?__VIEWSTATE=”會將__VIEWSTATE作為請求的鍵,其值為“”,但是“?__VIEWSTATE”會認為其鍵為“null”,其值為“__VIEWSTATE”
if (
((collectionBasedOnMethod["__VIEWSTATE"] == null) && (collectionBasedOnMethod["__VIEWSTATEFIELDCOUNT"] == null)) && ((collectionBasedOnMethod["__EVENTTARGET"] == null) && !flag))
{
return null;
}
如上的條件意味著請求的鍵中同時沒有“__VIEWSTATE”,“__EVENTTARGET”,“__VIEWSTATEFIELDCOUNT”,並且flag為false則返回null。flag為false意味著沒有鍵為“null”值以“__VIEWSTATE”開頭並且也沒有值為“__EVENTTARGET”的鍵值對。
此處得出結論③如果QueryString或Form雖然有請求值,但是QueryString或Form中的Key沒有“__VIEWSTATE”和“__EVENTTARGET”和“__VIEWSTATEFIELDCOUNT”,並且沒有鍵為“null”值以“__VIEWSTATE”開頭並且也沒有值為“__EVENTTARGET”的鍵值對,則IsPostBack=false。
3.1.5 this.Request.QueryStringText.IndexOf(HttpResponse.RedirectQueryStringAssignment, StringComparison.Ordinal) != -1
if (this.Request.QueryStringText.IndexOf(HttpResponse.RedirectQueryStringAssignment, StringComparison.Ordinal) != -1)
{
collectionBasedOnMethod = null;
}
HttpResponse.RedirectQueryStringAssignment的值為“__redir=1”,上面的程式碼的意思是如果QueryStringText中包括包括“__redir=1”則返回null。在HttpRequest.Redirect中會判斷如果IsPostBack為true,並且URL中不包含有“__redir=1”時,會給URL中增加“__redir=1”。一般情況下我們使用request.Redirect遷移到的頁面都應該是IsPostBack=false,有一種特殊的情形是使用request.Redirect遷移到當前頁,此時IsPostBack為true。此種情況發生時在request.Redirect中給URL中增加“__redir=1”。執行到page. ProcessRequestMain時會重新將IsPostBack判斷為fales。
此處得出結論④使用Response.Redirect方式向自畫面遷移時,此時IsPostBack=false。
此時大家可能會有疑問為什麼使用Response.Redirect方式向自畫面遷移時要特殊處理,使用Response.Redirect向其他畫面遷移為什麼不要。使用Response.Redirect向其他畫面遷移時Response.Form=null,Response.QueryString=null,所以可以判斷是IsPostBack=false。但是使用Response.Redirect方式向自畫面遷移時Response.QueryString<>null,所以要特殊判斷。
3.2 this._isCrossPagePostBack
if (this._isCrossPagePostBack)
{
return true;
}
在Page的PreviousPage屬性中會對_isCrossPagePostBack進行設定,具體程式碼如下:
public Page PreviousPage
{
get
{
…
ITypedWebObjectFactory vPathBuildResult = (ITypedWebObjectFactory) BuildManager.GetVPathBuildResult(this.Context, this._previousPagePath);
if (typeof(Page).IsAssignableFrom(vPathBuildResult.InstantiatedType))
{
this._previousPage = (Page) vPathBuildResult.CreateInstance();
this._previousPage._isCrossPagePostBack = true;
this.Server.Execute(this._previousPage, TextWriter.Null, true, false);
}
}
return this._previousPage;
}
}
在發生跨頁面提交的時候,當訪問PreviousPage屬性的時候源Page的IsCrossPagePostBack會被設定true。此處得出結論⑤發生跨頁提交(CrossPagePostBack),當訪問PreviousPage屬性的時候,對於源Page,IsPostBack=true。
3.3 this._pageFlags[8]
if (this._pageFlags[8])
{
return false;
}
在Page. ProcessRequestMain中有如下的程式碼片斷對_pageFlags[8]進行賦值。
else if (!this.IsCrossPagePostBack)
{
VirtualPath path = null;
if (this._requestValueCollection["__PREVIOUSPAGE"] != null)
{
try
{
path = VirtualPath.CreateNonRelativeAllowNull(
DecryptString(this._requestValueCollection["__PREVIOUSPAGE"]));
}
catch (CryptographicException)
{
this._pageFlags[8] = true;
}
if ((path != null) && (path != this.Request.CurrentExecutionFilePathObject))
{
this._pageFlags[8] = true;
this._previousPagePath = path;
}
}
}
解密發生異常時_pageFlags[8]為true這種異常發生的可能性比較小我們忽略,重點看另外一種情形,將這種情形的所有條件結合起來就是IsCrossPagePostBack=false && _requestValueCollection["__PREVIOUSPAGE"] != null && path != null && (path != this.Request.CurrentExecutionFilePathObject)。發生跨頁提交時對於目標頁面IsCrossPagePostBack=false,此時源頁面的"__PREVIOUSPAGE"等資訊會提交給目標頁面,所以_requestValueCollection["__PREVIOUSPAGE"] != null。此時當前請求的CurrentExecutionFilePathObject是根據目標頁的路徑生成的,與使用_requestValueCollection["__PREVIOUSPAGE"]生成的path物件不同。
此處得出結論⑥發生跨頁提交(CrossPagePostBack)時目標頁面是IsPostBack=false。為什麼需要對CrossPagePostBack的目標頁面做這樣的處理呢?發生CrossPagePostBack時,會將源頁面的資訊提交給目標頁面此時Request.Form!=null,而且包括__VIEWSTATE等鍵按照其他的規則會判斷為IsPostBack=true,所以需要對CrossPagePostBack的目標頁面做特殊的判斷。
3.4 (this.Context.ServerExecuteDepth <= 0) || ((this.Context.Handler != null) && (base.GetType() == this.Context.Handler.GetType()))
在HttpServerUtility中有如下的程式碼對Context. ServerExecuteDepth進行了操作。
public void Execute(string path, TextWriter writer, bool preserveForm)
{
…
try
{
this._context.ServerExecuteDepth++;
handler = this._context.ApplicationInstance.MapHttpHandler(this._context, request.RequestType, path3, filename, useAppConfig);
}
finally
{
this._context.ServerExecuteDepth--;
}
…
}
在HttpServerUtility.ExecuteInternal中也有一處對Context.ServerExecuteDepth類似的操作。HttpServerUtility.Execute會呼叫HttpServerUtility.ExecuteInternal。從此可以看出Context.ServerExecuteDepth是表示Server.Execute中的執行深度。在呼叫Server.Execute時Context.ServerExecuteDepth>0。另外呼叫Server.Execute後Context.Handle中儲存的還是原來的頁物件,也就是說base.GetType()!= this.Context.Handler.GetType()。這樣對於Server.Execute來說this.Context.ServerExecuteDepth <= 0) || ((this.Context.Handler != null)這個條件為false。此處得出結論⑦使用Server.Execute遷移到的頁面其IsPostBack=false。此處我們會有疑問,為什麼需要對Server.Execute進行特殊的判斷呢?理由是使用Server.Execute時會將源Page中的隱含域提交,此時Request.Form!=null,而且包括__VIEWSTATE等鍵按照其他的規則會判斷為IsPostBack=true。
3.5 this._fPageLayoutChanged
fPageLayoutChanged從這個變數的字面意思來看是Page的Layout發生了變化。
在Page.LaodAllState中程式碼片斷如下:
private void LoadAllState()
{
…
string s = (string) second.First;
int num = int.Parse(s, NumberFormatInfo.InvariantInfo);
this._fPageLayoutChanged = num != this.GetTypeHashCode();
…
}
其意思是現在得到的HashCode和儲存在ViewState中的HashCode不一致時fPageLayoutChanged=true。GetTypeHashCode()會返回一個HashCode,而且這個方法是對aspx進行編譯的時候產生的,只有在頁面上的元素髮生了變化的時候其返回的值會發生變化。此處得出結論⑧在Page執行期間其對應的DLL被更新了並且Page的樹結構發生過變化,這種情況下請求時IsPostBack=false。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12639172/viewspace-659712/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- IsPostBack深入探討
- Java執行緒的深入探討 (轉)Java執行緒
- 工程投標基本策略探討(轉)
- B/S模式安全性探討 (轉)模式
- Bash 例項:探討 ebuild 系統(轉)UI
- 科研專案管理方法探討(轉)專案管理
- LINUX LILO若干問題探討 (轉)Linux
- 對軟體專案管理的探討 (轉)專案管理
- 專案經理責、權、利探討(轉)
- 工程專案成本控制若干方法探討(轉)
- 工程總承包管理執行探討(轉)
- 關於 DDoS 攻擊事件的探討(轉)事件
- 對軟體專案管理的探討(轉)專案管理
- 專案團隊的信任問題探討(轉)
- 軟體開發週期估算及探討(轉)
- 對軟體專案管理的探討(1)(轉)專案管理
- 對軟體專案管理的探討(2)(轉)專案管理
- 關於.Net中屬性的使用探討(一) (轉)
- 關於.Net中屬性的使用探討(二) (轉)
- 對日軟體外包專案問題探討(轉)
- 關於 js 物件 轉 字串 和 深拷貝 的 探討JS物件字串
- Object Pascal中String型別的內幕探討 (轉)Object型別
- 【轉】關於oracle中Move機制的一點探討Oracle
- 以例項探討中小企業ERP應用(轉)
- 基於DirectDraw的Gamma calibrator機制之探討 (轉)GAM
- 國內應用軟體開發管理的探討 (轉)
- 關於多型實現Singleton模式的探討 (轉)多型模式
- 分享交換機 VLAN 配置基礎及例項探討(轉)
- 全面探討PL/SQL的複合資料型別(轉)SQL資料型別
- 探討Windows 2000 無人參與安裝(轉)Windows
- 資料備份方法及災難恢復探討(轉)
- 深入探討控制反轉(IOC)與依賴注入(DI)模式原理與應用實踐依賴注入模式
- 資料倉儲資料質量的問題探討(轉)
- 有關ERP應用外掛開發的探討(轉)
- ERP專案實施風險分析與控制方法探討(轉)
- 我的理解——關於“ERP過時論”的探討(轉)
- Oracle作業(JOB)更新next_date的探討(轉載)Oracle
- 掌握時間與空間:深入探討Golang中的時間戳與時區轉換Golang時間戳