Pozor na Response.Redirect(), Response.End() a obsluhu výjimek v ASP.NET

Jak myslíte, že dopadne následující příklad po kliknutí na tlačítko? (Stránka obsahuje jen label MyLabel a button MyButton)

public partial class _Default : System.Web.UI.Page { protected override void OnLoad(EventArgs e)     { base.OnLoad(e);         MyLabel.Text = (string)Session["OK"];     } void MyButton_Click(object sender, EventArgs e)     { try         {             Session["OK"] = "ok";             Response.Redirect("~/");         } catch         {             Session["OK"] = "exception";         }     } }

Mnohé z Vás asi překvapím, když řeknu, že do Session["OK"] se uloží "exception" a ten se v dalším requestu i zobrazí.

Response.Redirect(), resp. metoda Response.End(), kterou Redirect sám volá, totiž funguje tak, že vyvolá ve webové aplikaci interní výjimku (Thread.CurrentThread.Abort()), která je samotnou webovou aplikací zpracovávána tak, aby bylo dosaženo kýženého efektu, tj. aby se vykonávání kódu zastavilo v daném místě a další kód se nevykonal.

Potíž však nastane v okamžiku, kdy sami obalíme volání Response.Redirect() či Response.End() zachytáváním výjimek a nespecifikujeme dostatečně typ výjimek, které chceme zachytávat. Pokud necháme chytat výjimky všechny, uvedeme jako typ Exception, pak se dočkáme nežádoucího efektu, kdy nám volání Response.Redirect()/End() způsobí vykonání obsluhy výjimky, blok catch. Zachycení oné speciální interní výjimky je totiž v call-stacku poměrně vysoko a náš catch blok se dostane na řadu dříve (Zajímavé nicméně je, že přestože náš catch block výjimku obslouží, další kód za Redirect()/End() se přesto nevykoná. Interní výjimka je totiž použita pouze jako nosič informace, ale Thread.Abort() proběhne v nativním kódu úspěšně).

Východiskem je tedy obsluhovat pouze specifické typy výjimek, tak, jak to ostatně obecné guidelines doporučují pro všechny situace.

Robert Haken, ASP.NET MVP