最近写一个login用的handler,发现了一个以往没太注意的问题:
{
try
{
// Identity authentication
if (!AuthenticateIdentity(context))
{
context.Response.Redirect(Page1);
}
// Check parameters
if (!TryCheckParams(context))
{
context.Response.Redirect(Page2);
}
// Valid login
context.Response.Redirect(Page3);
}
catch (Exception ex)
{
DoSth();
context.Response.Redirect(Page4);
}
}
如上,我们常希望满足不同的条件就redirect到不同的page去,以上代码运行起来似乎没什么问题,页面能够正常跳转,但仔细检查error log会发现有很多异常抛出:ThreadAbortException (0): [mscorlib] Thread was being aborted.
原因就在于Response.Redirect 内部调用了Response.End,而Response.End 方法停止页面的执行,并将该执行变换到应用程序的事件管线中的 Application_EndRequest 事件。 Response.End 后面的代码行将不执行。
网上提供有解决方案是调用Response.Redirect的重载方法Redirect(string url, bool endResponse)并传入false,阻止对Response.End的调用。但在本实例中,这种方法也不能解决问题,因为我方法里面的逻辑判断是线性的if...if...if...,这就导致Response.Redirect可能被执行多次从而产生异常。要避免此错误,可以使用嵌套语句if...else if...else...,这样可以避免重复执行Redirect,但对于复杂的逻辑,这个嵌套会很复杂,非常不易于阅读。那么怎么办呢?我后来想到整个方法只在最后做Rediret跳转,而在前面所有的条件分支中只设置redirectURL,然后用goto语句强行转移到最后面的跳转语句。这样问题确实解决了,但目前很多公司都不建议使用goto语句,认为它会造成程序逻辑的混乱,不易于阅读和维护。
其实最佳方案是将以上代码提取到一个单独的方法HandleRequest(),这个方法执行所有的逻辑并返回最终的redirectURL,而ProcessRequest方法调用HandleRequest获取redirectURL并执行跳转即可。
参考:http://www.cnblogs.com/rolinson/archive/2005/01/22/95710.html