針對無效的 viewstate 問題進行疑難解答

本文介紹在 Microsoft ASP.NET 應用程式中偵錯及解決 viewstate 問題的技術。

原始產品版本: ASP.NET
原始 KB 編號: 829743

簡介

Viewstate 是 ASP.NET 中的一項功能,可讓頁面自動保留狀態,而不需要依賴伺服器狀態 (例如會話狀態) 。 不過,與 viewstate 相關的問題可能難以偵錯。 在大部分情況下,當 Viewstate 發生問題時,您會在網頁瀏覽器中收到下列錯誤訊息,但幾乎無法指出造成此問題的原因:

此頁面的 viewstate 無效,而且可能已損毀。

本文說明可用於偵錯和解決 viewstate 問題的一些技術。

如果您是在 Web 伺服陣列中執行,請設定 validationKey 屬性

在 Web 伺服陣列中,每個用戶端要求可以在每次回傳時移至不同的電腦。 由於此行為,您無法validationKeyMachine.config 檔案中將 屬性保留為 AutoGenerate 。 相反地,您必須將 屬性的 validationKey 值設定為 Web 伺服器陣列上所有機器共用的固定字串。

請勿將動態產生的類型儲存在 Web 伺服陣列的 viewstate 中

當 ASP.NET 以動態方式編譯檔案時,檔案會內建在具有隨機名稱的元件 (例如,檔名可能會 jp395dun.dll) 。 如果您正在執行 Web 伺服數位,相同的檔案會編譯成具有不同隨機名稱的元件。 一般而言,這不是問題,因為沒有人對這些元件名稱進行假設。 但是,如果您曾經使用二進位串行化將動態編譯的類型放入 viewstate 中,則元件的名稱會包含在 viewstate 數據中。 當該 viewstate 稍後傳送至 Web 伺服陣列中的不同伺服器時,無法還原串行化 viewstate,因為它使用不同的元件名稱。

最佳解決方法是避免使用二進位串行化。 即使您未遇到此問題,二進位串行化也會使用許多資源。 相反地,請將您在 viewstate 中放置的內容限制為數位、配對、三重和簡單類型的組合, (例如,字串、int 和其他類型) 。 System.Web.UI.PairSystem.Web.UI.Triplet 是 Viewstate 引擎可以有效率地處理的簡單包裝函式類型。

避免這個問題的另一個修正方法是將您要儲存在 viewstate 中的類型移至先行編譯元件,不論是在檔 Bin 夾或快取中 Global Assembly 。 此修正無法解決效能問題,但可保證所有計算機上的元件都具有相同的名稱。

注意事項

如果您將複雜的數據類型儲存在 viewstate 中,並遇到此問題,呼叫堆疊資訊會包含類似下列的堆疊:

[FileNotFoundException: Could not load file or assembly 'App_Web_fx--sar9, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.]
 System.RuntimeTypeHandle._GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark, Boolean loadTypeFromPartialName) +0
System.RuntimeTypeHandle.GetTypeByName(String name, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark) +72
System.RuntimeType.PrivateGetType(String typeName, Boolean throwOnError, Boolean ignoreCase, Boolean reflectionOnly, StackCrawlMark& stackMark) +58
System.Type.GetType(String typeName, Boolean throwOnError) +57
System.Web.UI.ObjectStateFormatter.DeserializeType(SerializerBinaryReader reader) +192 
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +943 
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +384 
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +198 
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +210 
System.Web.UI.ObjectStateFormatter.DeserializeValue(SerializerBinaryReader reader) +198 
System.Web.UI.ObjectStateFormatter.Deserialize(Stream inputStream) +142

viewstate 機器驗證程式代碼 (MAC) 功能的目的是要讓客戶端無法傳送包含惡意 viewstate 的要求。 根據預設,此功能會在 Machine.config 檔案的下列旗標中啟用。

enableViewStateMac="true"

判斷您要處理的問題是否與 MAC 功能相關,最簡單的方式是關閉此功能。 若要這樣做,請將 Machine.config 檔案中的旗標變更為下列程序代碼。

enableViewStateMac="false"

如果您不再收到 viewstate 錯誤,則問題與 MAC 功能有關。

重要事項

只關閉 viewstate MAC 功能,以協助診斷問題。 您不應該關閉 viewstate MAC 來解決此問題。 如果您這樣做,可能會造成安全漏洞。 如需詳細資訊,請參閱 建置安全 ASP.NET 應用程式:驗證、授權和安全通訊

如果您關閉 viewstate MAC 功能,然後針對未使用 HTML 編碼的控件使用 viewstate (例如標籤件) ,攻擊者可以竄改 viewstate 數據,並可將任意數據放在 viewstate 中。 這個任意數據會譯碼,然後由控件在呈現張貼的頁面時使用。 因此,除非您努力防止攻擊,否則攻擊者可以將腳本插入應用程式。 例如,攻擊者可以譯碼數據、將腳本插入標籤所在的數據中,然後從網站連結到該數據。 任何單擊連結的人,都會成為腳本插入攻擊的犧牲者,可能會竊取其驗證 Cookie 或會話標識符。 腳本也可以讓攻擊者改變使用 viewstate 之控件的狀態數據,而應用程式特定的攻擊可能會因此發生。

一般而言,Microsoft 建議您不要關閉 viewstate MAC 功能,除非您確信您已針對未將輸出編碼的所有控件停用 viewstate (例如,DataGrid 控件、DataList 控件、捲標控件和其他控件) 為 HTML,或您一律會在每個要求上明確地將其值設定為已知安全的專案。

確切判斷當您收到錯誤訊息時發生的例外狀況

可惜的是,本文簡 一節中提及的無效 viewstate 錯誤訊息並不具資訊性。 錯誤訊息是由處理 viewstate 時所產生的一些例外狀況所造成。 問題在於正在取用例外狀況,而且其詳細數據會在錯誤訊息中遺失。

藉由使用調試程式,您可以判斷原始例外狀況。 若要這樣做,您必須將調試程式附加至 ASP.NET 程式 (Aspnet_wp.exeW3wp.exe) ,然後將其設定為攔截所有例外狀況。 調試程式可能會在一些不相關的例外狀況停止,但最後會叫用 viewstate 例外狀況,並提供有用的信息來進行疑難解答。

下列步驟是使用運行時間調試程式 (Cordbg.exe) 的範例。

  1. 在命令提示字元中 iisreset ,執行 命令以確定您已獲得良好的起點,然後流覽至您網站上的頁面。

  2. 執行 cordbg.exe

  3. 輸入 <pro>,然後按 ENTER。 隨即會出現受控進程清單。 您應該會看到 Aspnet_wp.exeW3wp.exe 程式。 請記下其 PID。

  4. <PID>輸入 。

    注意事項

    PID 取代為步驟 3 中註明的 PID。

  5. 輸入 ca e 以指示 Cordbg.exe 在所有例外狀況上中斷,然後輸入 <gto> 執行進程。

  6. 每當您收到例外狀況時,請輸入 <w> 以查看堆疊。 如果堆疊是 viewstate 例外狀況, (在堆疊) 上尋找 LoadPageStateFromPersistenceMedium ,請從命令視窗複製所有例外狀況和堆棧資訊,然後儲存資訊。 此資訊可協助您了解問題。 如果例外狀況不相關,請輸入 <g>

嘗試將 viewstate 儲存在會話中

根據預設,viewstate 會以傳送至瀏覽器的字段來回傳 <input type=hidden> 送。 瀏覽器接著會在下一個要求上將字段傳送回伺服器。 在某些情況下,此 viewstate 可能會變大,並成為潛在的問題來源。 某些瀏覽器無法處理這類大型隱藏字段 (和產生的大型要求) ,而且瀏覽器可能會截斷 viewstate。 截斷 viewstate 會導致「viewstate 損毀」錯誤訊息。 此行為最有可能發生在較簡單的瀏覽器中。 例如,此行為可能會發生在 PDA 上的瀏覽器中。

若要判斷您是否遇到這類問題,請嘗試將 viewstate 儲存在會話中。 下列範例示範如何執行這項操作。

<%@ language=c# debug=true %> 

<script runat=server> 
protected override object LoadPageStateFromPersistenceMedium() 
{ 
    return Session["_ViewState"]; 
}

protected override void SavePageStateToPersistenceMedium(object viewState) 
{ 
    Session["_ViewState"] = viewState; 
}

void TextChanged(object o, EventArgs e) 
{ 
    Response.Write("TextChanged"); 
} 
</script> 
<form runat=server> 
<asp:button text=Test runat=server/> 
<asp:textbox ontextchanged=TextChanged runat=server/> 
<input type=hidden name=__VIEWSTATE> 
</form> 

判斷問題是否由背景工作進程回收所造成

請考慮下列情節:

  • 您在 Microsoft Internet Information Services (IIS) 6.0 下執行 ASP.NET。
  • 應用程式集區是在本機系統帳戶、網路服務帳戶或系統管理層級帳戶以外的身分識別下執行。
  • 項目的 validationKey 屬性 <machineKey> 在組態檔中設定為 AutoGenerate

在此案例中,下列程式會導致發生 viewstate 錯誤:

  1. 用戶瀏覽頁面。
  2. 裝載 ASP.NET 應用程式回收的背景工作進程。
  3. 用戶回傳頁面。

作為此案例的因應措施,請在組態檔中使用明確 validationKey 屬性。

參考資料