IsPostBack介紹  

2010-01-27 10:36:19|  分類: 技術交流 |  標簽: |字號 訂閱

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。  就是判斷當前頁面是不是你提交過后再次由服務器回傳過來的

你直接在網址里輸入一個頁面地址訪問頁面的時候
這時在服務器端創建的頁面對象的IsPostBack屬性為false
在你對這個頁面進行提交的時候
會把你頁面控件里的一些值啊什么的東西回傳給服務器
然后服務器就又創建了一個新的頁面對象
并且從ViewState中讀取回傳到服務器的信息
這個新的頁面對象的IsPostBack屬性就為true

對了
如果對一個頁面對象的生命周期了解的話就很好理解
你可以去查下相關的資料
要知道各個頁面事件都是在什么時候引發的
以及在不同的時期都完成了哪些工作
比如什么時候讀取ViewState
什么時候執行控件事件
什么時候保存ViewState
什么時候呈現頁面(也就是把網頁發送給客戶端)

這個說起來還比較麻煩 >_<
自己試試吧~~
對這個理解清楚的話
那ASP.NET頁面的回傳機制也就有個大概了解了
這樣對今后一些問題的解決也會很有幫助