首页 新闻 搜索 专区 学院

Response.End()后,是否停止执行?MVC与WebForm不一致。

3
悬赏园豆:100 [已解决问题] 解决于 2013-07-05 18:58
在WebForm中,执行Response.End()会停止该页的执行,这导致其后的代码不会被执行。我经常利用这一特性,方便地从多层的调用中直接退出,而无需逐层返回至最初的调用方。
但是,在MVC4中,我尝试了所有能想到的方式去测试Response.End(),其后的代码总是会执行,这一行为与WebForm不一致。
请问:
1、在MVC4中,为什么Response.End()之后的代码会被执行?
2、在MVC中,如何让Response.End()之后的代码不被执行?
3、如果MVC中无法让Response.End()之后的代码不被执行,那么,有什么替代的方案来实现“其后的代码不被执行”的功能?

 

附:
阅读至此的园友,即使暂未找到上述3题的答案,也请花点时间,和更多的园友们分享你对以下3题的答案:
4、最近1年内你经手过的项目,百分之几采用MVC、百分之几采用WebForm、以及其它?
5、最近1年内你经手过的项目,百分之几采用ADO.NET、百分之几采用Entity Framework、以及其它?
6、如果在过去的项目中你已使用MVC,请问,你是否遇到上述“Response.End()在MVC与WebForm中行为不一致”的问题?
问题补充:

---------------问题未解决,换个方式请教大家---------------

学习、使用Asp.Net近6年了,这是第一次正式在网上提问。因为这是第一次遇到自己查资料解决不了的问题。
真心感谢imfunny、空葫芦、幻天芒、bree、s_p花时间脑力为我解惑。
但这个问题还是没有解决。
看到alone__的回复,我意识到可能我这问题问得不好,因此,就上述问题,我写了两段伪代码,请大家再帮我看看。谢谢alone__的指正。

 

以下伪代码功能说明:
客户端用ajax向服务器发出POST请求,服务器返回表示执行成功或失败的字符串信息。客户端收到服务器返回的字符串,alert(),完毕。
客户端代码简单,这里就省略了。
下面的服务器代码共2组,第1种是ashx方式的,第2种是mvc方式的。

----------ashx方式的伪代码,用Response.End()从任何深嵌套中立刻退出,无需逐层依次返回----------

public class HandlerTest : IHttpHandler
{

    public bool IsReusable { get { return false; } }

    public void ProcessRequest(HttpContext context)
    {
        Method0();
        Method1();
        Method2();
        //Method1、Method2与Method0类似。实际业务中此处可能会有更多类似的代码需要执行。
        context.Response.Write("All methods success.");
    }

    private void Method0()
    {
        //do something.
        if(执行失败)
        {
            HttpContext.Current.Response.Write("Method 0 failure.");
            //停止后续代码的执行、直接输出至客户端,无需逐层返回调用方:
            HttpContext.Current.Response.End();
        }
        //do something.
    }

}

---------------MVC方式的伪代码---------------

public class TestAjaxController : Controller
{

    public ActionResult Test()
    {
        string msg = "All methods success.";
        if(Method0())
        {
            if(Method1())
            {
                if(Method2())
                {
                    //更多的Method需要执行。而且,如果前面的Method执行失败,必须停止执行后面的Method。
                }
                else
                {
                    msg = "Method 2 failure.";
                }
            }
            else
            {
                msg = "Method 1 failure.";
            }
        }
        else
        {
            msg = "Method 0 failure.";
        }
        return Content(msg);
    }

    private bool Method0()
    {
        //do something.
    }
    //Method1、Method2与Method0类似。

}

---------------代码演示完毕---------------
请大家用更优雅的MVC代码帮我实现上述业务需求,谢谢。

OldStudent的主页 OldStudent | 初学一级 | 园豆:112
提问于:2013-06-27 13:43
< >
分享
最佳答案
1

你只用了Response.End(),但是return了吗?

————————————————————————————

100% ASP.NET MVC,其他的都是组件或者Console,不涉及webform或者MVC

30%ADO.NET,70%是一种特别的支持Fluent API的ORM

————————————————————————-————

update at 7.1

ashx那里“//停止后续代码的执行、直接输出至客户端,无需逐层返回调用方:”应该是不准确的,你就算在method0里面使用了response.end(),但是method1和method2还是执行了的,可以用Debug.WriteLine()看一下调试窗口输出的。

MVC部分的伪代码,为什么不改用throw exception的方式实现呢,如果需求复杂,可以用自定义的异常。

public class HomeController : Controller
    {
        public ActionResult Index()
        {
            string msg = "All methods success.";

            try
            {
                Method0();
                Method1();
                Method2();
            }
            catch (Exception e)
            {
                msg = e.Message;
            }
            return Content(msg);
        }

        private bool Method0()
        {
            Debug.WriteLine("Method 0 process.");
            bool flag = true;
            if (!flag)
            {
                throw new Exception("Method 0 failure");
            }
            return flag;
        }

        private bool Method1()
        {
            Debug.WriteLine("Method 1 process.");
            bool flag = false;
            if (!flag)
            {
                throw new Exception("Method 1 failure");
            }
            return flag;
        }

        private bool Method2()
        {
            Debug.WriteLine("Method 2 process.");
            bool flag = true;
            if (!flag)
            {
                throw new Exception("Method 2 failure");
            }
            return flag;
        }
    }
收获园豆:55
空葫芦 | 初学一级 |园豆:9 | 2013-06-27 14:25
空葫芦 你好!非常高兴你的关注。

一、关于Response.End()
最近6年一直在WebForm中使用Response.End(),从未遇到Response.End()之后的代码还能被执行的情况。
今天在VS2008与VS2012的WebForm中、尝试System.Diagnostics的Debug和Trace进行各种Write(),无论Debug还是Release,从未遇到Response.End()之后的代码还能被执行的情况。

所以我的结论是:Response.End()之后的代码是否会被执行,在WebForm与MVC中不一致:前者总是不执行、后者总是执行。
如果大家有不同的结论,请给出完整代码。

二、关于throw Exception
我提问时所使用的示例的本质是:在WebForm中我用Response.End()来控制程序的流程,但在MVC中无法如此便利地控制程序流程。
之所以没有考虑使用异常,是因为我总是不愿意用异常来控制程序的流程。

——————————————— 关键 ———————————————
请问除了使用异常,MVC中,是否还有更优雅的方式实现上述业务流程?
OldStudent | 园豆:112 (初学一级) | 2013-07-01 15:28

@OldStudent: 一试之下,还真的发现了点有意思的问题。我们说得似乎都不全对,LZ可以先看一下这几个链接。我稍后再补充回复

http://q.cnblogs.com/q/32216/

http://bbs.csdn.net/topics/330087387

http://www.shubho.net/2011/01/handling-threadabortexception-with.html

http://msdn.microsoft.com/zh-cn/library/system.web.httpresponse.end(v=vs.100).aspx

 

————————-————————————

update:7-1 18:17

MVC中调用的Response.End是这里的

http://msdn.microsoft.com/zh-cn/library/system.web.httpresponsebase.end(v=vs.100).aspx

应该和HttpContext.Current.Response.End()的实现有别

—————————————————————

update:7-1 18:28

在MVC中使用System.Web.HttpContext.Current.Response.End();显式调用和非MVC一样的这个方法,但是并没有引发异常。不知是何原因,

没有引发异常,也就不能去控制“不执行其后代码“这个流程控制~~~

google了一下,看到了不少资料,明天再看了先下班。

在此向LZ致歉,不管由于何种原因,而且个人觉得用return控制流程更为正道,但是之前的回答的确有错漏之处,并不完全。

空葫芦 | 园豆:9 (初学一级) | 2013-07-01 18:00
你好 空葫芦,非常感谢!

如你所说,我也发现从.NET Frameworks 3.5开始多了HttpResponseBase这个类。

MSDN:在WebForm中,HttpResponse 类的方法和属性通过 HttpApplication、HttpContext、Page 和 UserControl 类的 Response 属性公开。
测试发现,不出所料,object.ReferenceEquals(Page.Response, HttpContext.Current.Response)的结果为true。

但是,在MVC中,出现了以下2个:
System.Web.HttpResponse
System.Web.HttpResponseBase
前者是从.NET Framework 1.1开始就有的,包括Page.Response、HttpContext.Response等。
后者是从.NET Framework 3.5新增的,从MVC的View中通过WebPageRenderingBase.Response可get。
这新旧2种Response的End(),在MVC中都不再停止后续代码的执行,与WebForm中不一致了。

我还发现:
将HttpContext.Current.Response.End()置于try块中,
在WebFrom中可catch到[System.Threading.ThreadAbortException] = {由于代码已经过优化或者本机框架位于调用堆栈之上,无法计算表达式的值。};
但是在MVC中catch不到任何异常。
我理解为:
在WebFrom中,因为抛出的这个特殊的异常直接转到框架去处理了,所以后续代码不被执行。
而在MVC中,似乎没有抛出这种异常、没有将流程转到框架去处理,所以后续代码继续被执行。
不知道这是不是真正的原因?不知道微软为什么偷偷地要改变End()的行为?

我面向对象很弱,每次尝试读微软的源码,自觉水平太太差,非常受伤,也就只能自己琢磨到这一步了,于是就有了这次在博客园的提问,期待着大家的帮助了,
OldStudent | 园豆:112 (初学一级) | 2013-07-02 00:02

@OldStudent: 现在只能确认以下几点,为了避免混淆方法直接带上完整的命名空间了

1.webform下使用System.Web.HttpContext.Current.Response.End()首先引发了一个异常,在异常处理完(执行完catch中代码)后到执行Application_EndRequest()

2.如果不使用System.Web.HttpContext.Current.Response.End(),自己人为地去throw一个异常而不捕获,可以在代码层面达到同样的效果(但是输出页面会报错),不知System.Threading.ThreadAbortException有何特殊之处(目前只知道他是继承于SystemException,)

3.MVC可能是因为如果没有return View()就不能把Model传到View,继而不能构造视图,因此采取了和webform不同的处理机制。

不管出发点如何,依然不清楚最核心最根本的原因,简单一句话说就是:是什么导致了System.Web.HttpContext.Current.Response.End()在webform下引发了异常而在ASP.NET MVC没有。

空葫芦 | 园豆:9 (初学一级) | 2013-07-02 12:45

@OldStudent:

http://blogs.msdn.com/b/rickandy/archive/2012/03/01/response-redirect-and-asp-net-mvc-do-not-mix.aspx

目前也只能确认是设计上的差异,或许深究到底(内部机制是怎么区分处理的)意义已经不大。因为就算MVC里面Response.End()之后停止执行,你总要return一个ActionResult吧。

如果非要实现和webform中一样的,你可以使用Thread.CurrentThread.Abort()方法,这个方法会引发一个ThreadAbortException异常,就像webform中那样。

此时页面会直接输出而没有和视图关联起来(提示的异常也和throw new Exception()不同),如果你的这个路由本身就没有视图部分,只是通过Action方法执行打印一些数据,这倒也不是不不行。

只是个人始终觉得有点别扭,不过个人能力所限,找不到更好的办法了。

空葫芦 | 园豆:9 (初学一级) | 2013-07-02 14:15
空葫芦你好,多谢你的帮助,让我弄明白了这个问题。
我在广州,请问你在哪个城市?不知道近不近,近的话请你喝茶/酒。
OldStudent | 园豆:112 (初学一级) | 2013-07-05 18:50

@OldStudent: 实在太客气啦。我现在也在广州,华景这边,喝茶/酒就不必啦,有什么技术会议可以相约参加下~~:)

空葫芦 | 园豆:9 (初学一级) | 2013-07-05 22:25
其他回答(8)
-1

1、End(),表示不往浏览器输出数据了。这个和 Response.Redirect(string url,bool endResponse),比较类似。

Response.End()不能终止整个代码的运行。

2、直接return即可。

3、Na

4、30%MVC,50%WebForms,其他WinForm

5、10%Ado.Net,30%LinqToSql,5%Entity Framework,30% iBatis,25%各种Orm。

6、暂时没,虽然是两种不同的开发模式,不过用了MVC之后,就不太喜欢WebForms了,现在的网页,对交互要求比较高,在WebForms中使用jquery等没在mvc中更简洁~

收获园豆:10
幻天芒 | 园豆:36652 (高人七级) | 2013-06-27 14:38

如果用goto的话,哈哈...

支持(0) 反对(0) 幻天芒 | 园豆:36652 (高人七级) | 2013-07-01 20:06
呵呵,我不抗拒使用goto,只是goto有范围限制,只能在方法体内跳转,无法跳转回方法外的调用方,上述业务逻辑中用不上。
支持(0) 反对(0) OldStudent | 园豆:112 (初学一级) | 2013-07-02 00:19

@OldStudent: 我发现我想多了,你哪儿想终止,直接return Content(msg);不就ok了?

支持(0) 反对(0) 幻天芒 | 园豆:36652 (高人七级) | 2013-07-02 00:22
0

http://msdn.microsoft.com/en-us/library/ms525405(v=vs.90).aspx

Response.End

Stops processing the .asp file and returns the current result

MVC  是管线设计   是 IHttpHandler -->MvcRouteHandler   类比 webform   ASHX

收获园豆:8
bree | 园豆:220 (菜鸟二级) | 2013-06-27 20:33
0

加retrun 吧

ef就算了  小项目还可以用用 是个花瓶

收获园豆:5
s_p | 园豆:279 (菜鸟二级) | 2013-06-27 21:05
0

看的好累,

收获园豆:2
alone__ | 园豆:60 (初学一级) | 2013-06-28 14:34
0

最近做项目遇到这个问题,   也感觉很困惑,搜到楼主这里来了。

 

 

 

的确,  在webform中  response.End() 不仅仅是终止输出,而同时函数也终止了;

基于此习惯,最近用MVC做手机APP的服务器端,大量用到 不同的条件下不同的 response.write(); response.End() ;

以为跟webform一样后面的函数就不会继续执行,结果测试不对, 检查之后发现在MVC中response.End() 之后的代码百分之百还会继续执行,    

最终我又回头把所有的response.End()后面增加了 return;  

孤城 | 园豆:215 (菜鸟二级) | 2013-12-03 09:36
0

good

francislee | 园豆:202 (菜鸟二级) | 2015-02-28 14:05
0

收藏 之前没注意这个问题

Lumia1020 | 园豆:189 (初学一级) | 2016-08-25 00:49
0

 ActionResult empty = new EmptyResult();return empty;重定向后,返回个这么个玩意儿。空

Garfield-加菲 | 园豆:204 (菜鸟二级) | 2017-06-05 14:34
清除回答草稿
   您需要登录以后才能回答,未注册用户请先注册