Hello World
Spiga

尽可能摆脱对HttpContext的依赖

2009-03-09 09:17 by 老赵, 9384 visits

我们继续《ASP.NET MVC单元测试最佳实践》,今天主要谈论HttpContext的依赖问题。

在ASP.NET中进行单元测试的天敌便是HttpContext,它是ASP.NET的核心,极端复杂,却无法进行Mock1——可见微软能够写出那么庞大的ASP.NET框架真不那么容易。现在这个状况改善了不少,因此大家已经可以使用System.Web.Abstractions.dll了,这个程序集中提供了对于HttpContext的抽象,也就是HttpContextBase抽象类。因此在ASP.NET MVC中,各种组件均依赖于HttpContextBase而不是HttpContext。这是一个优秀的做法,大家以后可以尽可能地摆脱HttpContext了。

不过这似乎又是一个悖论。虽然已经可以对HttpContext进行Mock(这点增强了可测试性),但是过度依赖HttpContext对于单元测试来说也是一个伤害。这是HttpContext对象的天性所致:它实在太复杂了。您应该已经察觉到,这是个集万千宠爱于一身的对象,从请求,回复,应用程序,缓存……几乎包含了Web应用程序需要的所有信息。如果要测试一个依赖于HttpContext的方法,您势必要为HttpContext的Mock对象填充各种信息——其复杂程度视业务而定。而且,Mock关注的是“行为”,也就是说它关注的是做一件事情所使用“路径”。那么如果做一件事情可以采用多个路径又会怎样?是否需要在测试之前准备好所有的路径,并且验证被测试的代码“采用了,并仅仅采用了其中一条路径”?因此,Stub慢慢进入人们的视线。Stub关注的是“状态”……这就是另一个话题了,还会涉及到采用Record & Replay还是Arrange-Act-Assert方式来进行单元测试,暂且不提。

之前谈到对视图进行单元测试时,老赵曾经谈起在视图中应该只使用ViewData中的数据。这不是第一次说起要放弃HttpContext了,自从有了“抽象”这一有利武器后,一切“不和谐”因素都能够被分离。试想在MVP模式中,View和Presenter都使用各自的抽象进行交互,一切Web控件,HttpContext等对象都不复存在了,大家眼中只有“数据”和“模型”。同样,在ASP.NET MVC的Action方法中,也不应该使用HttpContext,这是基于良好的“可测试性”而考虑的。您可能会想,现在的HttpContextBase对象已经可以Mock了啊。没错,它的确“可以”,但是这样做会引起单元测试代码的膨胀,因为测试代码中的相当部分必须关注在测试数据的准备,而不是被测试的功能上。对于一个Action方法来说,它关注的应该是用户与业务逻辑的交互,而不是“如何把HTTP请求转化为可用的数据”。其实说到底,还是要“分离关注点”。

在ASP.NET MVC中负责“转化数据”的层次为Model Binder。关于这一点,现有的“示例”大都关注把Form或QueryString中的数据转化为Action参数上,不过Model Binder可用的地方其实更多。例如在《最佳实践》的代码中,原本AccountController的Delete方法实现如下:

public ActionResult Delete(string userName)
{
    this.MiddleTier.UserManager.Delete(userName);

    Uri urlReferrer = this.Request.UrlReferrer;
    return this.Redirect(urlReferrer.ToString());
}

在删除了指定对象之后,页面将跳转到Url Referrer地址中。在上面的代码中,这个值将通过访问Request.UrlReferer来获得。这就使您的Action方法与HttpContext产生了依赖,因此它的单元测试代码就需要这样编写:

[TestMethod]
public void DeleteTest()
{
    string userName = "jeffz";
    Uri urlReferrer = new Uri("http://www.microsoft.com");

    var mockHttpContext = new Mock<HttpContextBase>();
    mockHttpContext.Setup(c => c.Request.UrlReferrer).Returns(urlReferrer);

    var mockController = this.GetMockController();
    mockController.Setup(c => c.MiddleTier.UserManager.Delete(userName)).Verifiable();
    mockController.Object.ControllerContext = new ControllerContext(
        mockHttpContext.Object, new RouteData(), mockController.Object);

    mockController.Object.Delete(userName)...
}

在单元测试代码中,我们Mock了一个HttpContextBase对象,让它的Request.UrlReferrer属性返回我们准备好的对象,再构造一个新的ControllerContext并交给Controller。而如果我们的UrlReferrer能够作为Delete方法的参数,那么单元测试代码就会一下子简单很多:

[TestMethod()]
public void DeleteTest()
{
    string userName = "jeffz";
    Uri urlReferrer = new Uri("http://www.microsoft.com");

    var mockController = this.GetMockController();
    mockController.Setup(c => c.MiddleTier.UserManager.Delete(userName)).Verifiable();

    mockController.Object.Delete(userName, urlReferrer)...
}

有些朋友可能会问,不就是从Request的UrlReferrer属性中取值吗?我们为什么要构造一个ControllerContext,不能直接设置Controller对象吗?例如这样就简单多了:

mockController.Setup(c => c.Request.UrlReferrer).Returns(urlReferrer);

似乎可行,不过您运行的时候就会发现,框架会抛出异常,说只有接口的成员,或可以override的成员才能够被Mock。没错,Controller的Request属性不是virtual的,无法override。Controller类如此设计是故意的,目的就是限制了可用的路径。试想,如果您Mock了Controller.Request属性,但是程序代码通过Controller.HttpContext.Request进行访问又怎么办呢?类似的做法还有对方法重载的设计。一般来说,都会把其中几个方法委托给其中唯一的方法,而只有那个方法是可以被override的。这样在编写测试时,我们仅有的Mock入口便确定了,避免了测试代码过度了解方法实现的问题。

回到正题。如果要让Delete方法接urlReferrer受参数,那么我们就要编写Model Binder相关的组件:

public class UrlReferrerModelBinder : IModelBinder
{
    public object BindModel(
        ControllerContext controllerContext,
        ModelBindingContext bindingContext)
    {
        return controllerContext.HttpContext.Request.UrlReferrer;
    }
}

并使其可以直接运用到Action的参数上:

public class UrlReferrerAttribute : CustomModelBinderAttribute
{
    private static UrlReferrerModelBinder s_modelBinder =
        new UrlReferrerModelBinder();

    public override IModelBinder GetBinder()
    {
        return s_modelBinder;
    }
}

于是乎,我们的Delete方法便可写为:

public ActionResult Delete(string userName, Uri urlReferrer)
{
    this.MiddleTier.UserManager.Delete(userName);
    return this.Redirect(urlReferrer.ToString());
}

如今的代码,无论是应用程序还是框架类库,都必须考虑“可测试性”这个要求。例如.NET 3.0的WF,由于其可测试性不佳一直为人所诟病。现在我们在编写程序时,要时刻询问自己:“这么做方便测试吗?”考虑到这个问题,可能您就会放心地做出某些抉择了2

 

注1:其实还是可以Mock的。例如Typemock使用Profiler的方式进行直接注入,可以Mock任何成员。不过,如果Moq等框架无法满足您的需要,一般便是您的设计有些问题了。

注2:例如,究竟让Action方法返回ActionResult,还是返回void,并直接通过Response输出呢?

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

64 条回复

  1. glonely
    *.*.*.*
    链接

    glonely 2009-03-09 00:49:00

    抢个沙发,睡觉

  2. glonely
    *.*.*.*
    链接

    glonely 2009-03-09 00:50:00

    头一回坐赵老师的沙发,哈哈.
    明天再仔细看.. :)

  3. 海洋——海纳百川,有容乃大.
    *.*.*.*
    链接

    海洋——海纳百川,有容乃大. 2009-03-09 00:51:00

    看看,明天继续看,今天太晚了,得好好休息了。

  4. 拼命占便宜
    *.*.*.*
    链接

    拼命占便宜 2009-03-09 00:51:00

    好吧.

  5. 小手冰冰凉
    *.*.*.*
    链接

    小手冰冰凉 2009-03-09 08:10:00

    来晚了。。。呵呵。。

  6. 第一控制.NET
    *.*.*.*
    链接

    第一控制.NET 2009-03-09 09:06:00

    不错。眼前一亮。以前我都是封死到其他地方去。

  7. 代震军
    *.*.*.*
    链接

    代震军 2009-03-09 09:12:00

    问题的确如此,我也考虑过这个问题,先收藏了:)

  8. 你好~~[未注册用户]
    *.*.*.*
    链接

    你好~~[未注册用户] 2009-03-09 09:28:00

    问老赵一个问题。
    您现在的项目(wodeyichu)是用的asp.net 的mvc框架吗?
    因为我们想在正式的项目中使用这个mvc框架,不知道该框架是否成熟。

  9. Modo[未注册用户]
    *.*.*.*
    链接

    Modo[未注册用户] 2009-03-09 09:31:00

    mvc 碰都没碰

  10. 老赵
    admin
    链接

    老赵 2009-03-09 09:31:00

    --引用--------------------------------------------------
    Modo: mvc 碰都没碰
    --------------------------------------------------------
    真可惜

  11. 老赵
    admin
    链接

    老赵 2009-03-09 09:31:00

    @你好~~
    1、不是,因为MVC框架出来的晚。
    2、成熟,因为其他领域已经存在很久了。

  12. binglingshui
    *.*.*.*
    链接

    binglingshui 2009-03-09 09:37:00

    回家接着看。
    上班有点忙!

  13. 海洋——海纳百川,有容乃大.
    *.*.*.*
    链接

    海洋——海纳百川,有容乃大. 2009-03-09 09:48:00

    老赵,最近有没有研究 dynamicdata 技术? http://www.asp.net/dynamicdata/

  14. Q.Lee.lulu
    *.*.*.*
    链接

    Q.Lee.lulu 2009-03-09 09:51:00

    快枪赵最近发文就是快

  15. 老赵
    admin
    链接

    老赵 2009-03-09 10:02:00

    @海洋——海纳百川,有容乃大.
    没有,计划等到正式版出来再说

  16. 对付[未注册用户]
    *.*.*.*
    链接

    对付[未注册用户] 2009-03-09 10:13:00

    请写些负载均衡 容错平衡 主机集群 数据库集群 应用服务器集群的主题,不要只讲那些开发应用的小技巧

  17. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-03-09 10:22:00

    如今的代码,无论是应用程序还是框架类库,都必须考虑“可测试性”这个要求
    --------------------------------------------------------------------------
    如果你一开始就是用TDD开发的,基本上能保证“可测试性”。

  18. 老赵
    admin
    链接

    老赵 2009-03-09 10:34:00

    --引用--------------------------------------------------
    对付: 请写些负载均衡 容错平衡 主机集群 数据库集群 应用服务器集群的主题,不要只讲那些开发应用的小技巧
    --------------------------------------------------------
    这些不是“小技巧”,是编程实践。
    你说的这些没有太大价值,我不是说这方面没有技巧或用途不大,但是我可以说我见过太多在这方面说白了就是堆砌现有产品。这方面靠说的话基本上都是纸上谈兵,例如您现在这样的名词列举,呵呵。
    要我吹嘘我也可以吹得天花乱坠,产品和技术列举的piapia的,可惜价值不大。反倒是掌握思维,受益无穷。
    所以我只会在小圈子里谈这些,欢迎加我msn单聊或交流心得之类的,呵呵。

  19. 老赵
    admin
    链接

    老赵 2009-03-09 10:36:00

    @对付
    好吧,其实这个理由是更重要的:我在这方面只有心得,没有创新,我写的出来的东西互联网上到处可以找到,因此拉不下脸写这些东西,呵呵。

  20. 风海迷沙
    *.*.*.*
    链接

    风海迷沙 2009-03-09 11:14:00

    在ASHX中当然使用Context输出了,在ASPX中当然使用RESPONSE输出。
    测试上很不一样么?

  21. 老赵
    admin
    链接

    老赵 2009-03-09 11:18:00

    --引用--------------------------------------------------
    rainnoless: 看着老赵的文章,我发现自己离服务器端越来越远了,呵呵。
    路过,没有看懂,因为好久没有接触过VS2003/.NET1.1以外的世界,呵呵。真相配置一套VS2010+.NET4.0体验一下感觉,呵呵。然后再学习老赵的学习精神,回顾到.NET3.5和C#3.0中来,呵呵。
    --------------------------------------------------------
    客户端也是一条光明大道阿

  22. 老赵
    admin
    链接

    老赵 2009-03-09 11:18:00

    --引用--------------------------------------------------
    风海迷沙: 在ASHX中当然使用Context输出了,在ASPX中当然使用RESPONSE输出。
    测试上很不一样么?
    --------------------------------------------------------
    没听懂你的意思。

  23. rainnoless
    *.*.*.*
    链接

    rainnoless 2009-03-09 11:18:00

    看着老赵的文章,我发现自己离服务器端越来越远了,呵呵。
    路过,没有看懂,因为好久没有接触过VS2003/.NET1.1以外的世界,呵呵。真相配置一套VS2010+.NET4.0体验一下感觉,呵呵。然后再学习老赵的学习精神,回顾到.NET3.5和C#3.0中来,呵呵。 不过暂时都是在梦中游荡,呵呵。

  24. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-03-09 12:17:00

    老赵 我来吐槽
    上次听了你的mvc讲座 我更加坚定了我的silverlight2 方向上的决心 亩哈哈
    mvc不是不好 就是不适合我这样的懒人
    我们项目组就在维护修改一个 asp+vb6 的mvc框架产品
    每天吐血的心都有啊!

  25. 老赵
    admin
    链接

    老赵 2009-03-09 12:25:00

    @韦恩卑鄙
    那么支持一下,别来和我抢生意,哈哈

  26. 韦恩卑鄙
    *.*.*.*
    链接

    韦恩卑鄙 2009-03-09 12:51:00

    恩 等着坐享老赵你的成果了 支持

  27. 水果阿生
    *.*.*.*
    链接

    水果阿生 2009-03-09 13:35:00

    @对付
    同学你比我幽默多了。早上吃的什么?

  28. 重典
    *.*.*.*
    链接

    重典 2009-03-09 14:40:00

    我现在是将所有涉及到HttpContext的Action都分离为单独的类

    这些类又抽像出接口,测试时是对这些接口建立新的Mock类

    这样实现了MVC的可测试性,但是类越来越多

    但是由于部分具体实现的类,如用户信息与HttpCOntext也存在直接依赖关系,而且也有的存在很多逻辑性的代码,需要测试,那我是否也要将这里再次解耦

    也就是说逻辑与僵化的代码相解耦,仅对逻辑进行测试

  29. 重典
    *.*.*.*
    链接

    重典 2009-03-09 14:44:00

    @韦恩卑鄙
    我也在维护这么个东西。。。。
    吐胆汁的心都有

  30. 老赵
    admin
    链接

    老赵 2009-03-09 14:49:00

    @重典
    如果有业务要依赖HttpContext,那么总归有东西要依赖HttpContext,只是使用一定抽象把HttpContext封装起来,再使用数据与逻辑进行交互。总之还是隔离,把数据的提取逻辑,和数据的使用逻辑隔离开来。这样两种逻辑可以分别地进行测试。
    我觉得你的做法应该也是可取的,具体使用什么方式,视情况而定,不妨举个简单的例子?

  31. 神之恩宠
    *.*.*.*
    链接

    神之恩宠 2009-03-09 15:27:00

    老赵,怎么不和我们把Oxite分析分析呀。。。。

  32. 老赵
    admin
    链接

    老赵 2009-03-09 15:50:00

    --引用--------------------------------------------------
    神之恩宠: 老赵,怎么不和我们把Oxite分析分析呀。。。。
    --------------------------------------------------------
    让它再发展发展吧,有机会再讨论。

  33. 廉毅
    *.*.*.*
    链接

    廉毅 2009-03-09 17:19:00

    你好啊请问能不能给微软mvc开发组个建议,在生成VIEW时可以选择用列说明(列注释)来生成列标题呢?因为只能用列的字段名生成列标题可能适合英语国家,但对于用中文的很不方便,生成后还要手工再改

  34. 老赵
    admin
    链接

    老赵 2009-03-09 17:23:00

    @廉毅
    这点修改很正常吧,本来就没有说直接用的。
    如果用注释的话,注释的格式呢?是不是又要追求灵活性。
    于其如此,不如自己写一个吧。

  35. 小洋
    *.*.*.*
    链接

    小洋 2009-03-09 20:14:00

    学习;)

  36. 你好~~[未注册用户]
    *.*.*.*
    链接

    你好~~[未注册用户] 2009-03-10 09:23:00

    老赵,请教一个问题。
    在mvc中,Controller是通过给方法加参数来接收url的参数。
    我现在遇到一个问题是我想在用户控件或是母模板中接收url参数。Request是接收不到。有什么方法啊?

  37. 老赵
    admin
    链接

    老赵 2009-03-10 09:45:00

    @你好~~
    View只应该从ViewData中获取数据。

  38. 你好~~[未注册用户]
    *.*.*.*
    链接

    你好~~[未注册用户] 2009-03-10 10:15:00

    场景是这样的。
    多个页面都要显示某个用户的10个好友,我希望使用用户控件获取受访用户的id显示他的好友信息。
    您有什么建议。谢谢!

  39. 老赵
    admin
    链接

    老赵 2009-03-10 10:17:00

    @你好~~
    asp.net mvc没有用户控件的概念。
    你需要在那些页面的Model中准备一组数据(例如n各用户表示好友),然后把这组数据交给Partial View来呈现。

  40. 廉毅
    *.*.*.*
    链接

    廉毅 2009-03-12 09:19:00

    没见过mvc做的企业应用的案例,见到的以blog之类的东西居多,企业应用价值开发不大,希望提供个mvc企业成功构架的实际案例,比如进销存、物流等,真怕mvc到最后也就是做做博客论坛之类的东西。。。

  41. 老赵
    admin
    链接

    老赵 2009-03-12 09:33:00

    @廉毅
    1、这是表现层技术,表现层技术与实际平台能力其实关系不大。
    2、MVC这种驱动方式,早在其他平台就被充分验证了。

  42. 撒旦之吻
    *.*.*.*
    链接

    撒旦之吻 2009-03-17 16:52:00

    在Webfrom当中本人对HttpContext依赖确实是重。。

    就像Community Server中都要把这个上下文包装。感觉用习惯,至于MVC当中的话,没有仔细看过,但是机制肯定是改变了。。。呵呵。。。待老赵分析。。

  43. 的发生地方[未注册用户]
    *.*.*.*
    链接

    的发生地方[未注册用户] 2009-03-18 15:40:00

    457457

  44. Zealic
    *.*.*.*
    链接

    Zealic 2009-04-02 16:28:00

    @JefferyZhao

    ControllerContext 的 Request 属性可以被 set,因此这里的论断:Request 无法 Mock 是不正确的。

    我们可以在测试开始之前(如 SetUp),或 TestMethod 的 Prepare 中实例化一个 RequestContext,它的构造器参数中的对象都是可以 Mock 的。

    顺带提一下,Moq 确实是好东西,我以为用的人很少,没想到老赵居然也用。

  45. 老赵
    admin
    链接

    老赵 2009-04-02 16:39:00

    @Zealic
    ControllerContext的Request属性?有这个属性吗?
    // moq使用已经很广泛了吧,它推广很迅猛的……

  46. 老赵
    admin
    链接

    老赵 2009-04-02 17:46:00

    @Zealic
    我说的不就是这个意思吗?我说的是:没有办法直接Mock那个Request属性,而只能通过Mock那个HttpContext进行,这么做的原因是保证了Mock的单一入口。
    你一定要说,通过HttpContext属性进行Mock,也就相当于Request属性能够Mock,这个……不纠缠于这个概念,呵呵。

  47. Zealic
    *.*.*.*
    链接

    Zealic 2009-04-02 17:50:00

    @Jeffrey Zhao
    -> 注一
    此外,我是反对使用 TypeMock 进行 Mock 的,虽然他的魔法很神奇(这东西还要钱)。
    原因是,在持续集成中,使用 NCover/PartCover 运行基于 TypeMock 的单元测试时,由于存在两个 Profiler ,会造成冲突。 导致无法运行结果与预期不相符。

    ->注二
    我觉得对于复杂结果使用 ActionResult,简单结果直接 Response。

  48. Zealic
    *.*.*.*
    链接

    Zealic 2009-04-02 18:00:00

    @Jeffrey Zhao
    确实 Request 不能 Mock,但是既然它已经"固定"依赖于外部 HttpContext,之所以提供这个属性就是为了让 Controller 能快速访问而已,又何必让它可以 override 呢?
    我想这也许就是 MVC Team 的考虑吧 :)
    又或者,MVC Team 设计时根本就没有考虑视图的单元测试(这事相当的吃力不讨好,个人认为测试下 500 OK 就好)
    再说,单元测试本身就要求编写者了解被测试单元的逻辑,自然也包括所有依赖。

  49. 老赵
    admin
    链接

    老赵 2009-04-02 18:08:00

    @Zealic
    你是不是没有看清我的文章……我说的完全就是你说的……
    每次测试的是Controller的一部分,为了测这一部分,会Mock另外一部分。

  50. Zealic
    *.*.*.*
    链接

    Zealic 2009-04-02 19:35:00

    @Jeffrey Zhao
    现在刚回家,仔细看了一遍

    是我钻牛角尖了

    就当刚才的讨论是演示代码膨胀和解决依赖的繁琐吧。

    此外还想问下,老赵是怎么解决单元测试中准备测试数据/模型的问题的?

    类似 ROR 那样用 xml 饭序列化对象、直接用代码构造或其他?

  51. 老赵
    admin
    链接

    老赵 2009-04-02 20:21:00

    @Zealic
    单元测试的话,数据一般都是硬编码的吧,还是我没有理解你的意思呢?

  52. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-04-02 21:30:00

    现在的测试框架都支持csv数据源吧
    新版的mbunit好像还支持xml得

  53. 老赵
    admin
    链接

    老赵 2009-04-02 23:29:00

    @Nick Wang
    支不支持一回事,需不需要是另一回事啊,而且我觉得我们在说的不是一个东西。兄弟你介绍一下?

  54. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-04-02 23:42:00

    @Jeffrey Zhao
    到底说的是啥啊,我有点糊涂了。你想让我介绍点什么?

  55. 老赵
    admin
    链接

    老赵 2009-04-03 08:23:00

    @Nick Wang
    介绍一下单元测试中对于测试数据的数据源的处理?

  56. Zealic
    *.*.*.*
    链接

    Zealic 2009-04-03 10:20:00

    准备测试数据是指包括准备数据库、数据连接、实体文件等一系列测试所必须的数据和外部依赖状态。

    现在大多情况都是直接用代码来写,这样很不好。
    理想的情况是,像 DbUnit 那样可以通过配置直接准备好需要的数据和外部依赖状态。目的就是,减少单元测试准备数据\状态代码的量。

    ROR 的 YAML Fixture 这一点上做的很好。

    像 MbUnit 虽然也有一些这样的功能(Attribute + XML),但首先要依赖于 MbUnit 这个框架,功能也不是太完善,所以依然不是我想要的;如果没有这方面的东西,也许我得自己写一个。

  57. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-04-03 11:09:00

    @Zealic
    为什么要用数据库呢?里面有什么?
    单元测试如果需要连接数据库就不是单元测试了。你可以将访问数据库的代码单独提取出来做成一个类,然后提取接口,这样要测试的类就会依赖接口了,然后就可以用mock来准备数据了

  58. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-04-03 11:14:00

    也可以用一个fake的数据库实现类来准备数据,在内存中准备数据,速度没有问题。 如果你是想测数据库搜索功能什么的,那就是数据库的单元测试了,应该测SP而不是.NET代码。

  59. Zealic
    *.*.*.*
    链接

    Zealic 2009-04-03 17:18:00

    @Nick Wang
    之所以要准备数据库,是因为要测试 DataAccess 的实现。否则就不能达到测试 生成/编写的 sql 语句的目的。你所说的 fake 数据库,大概以为我要测试的是逻辑层吧。

    你可以了解以下 DbUnit(Java, http://www.dbunit.org/),是一个很成熟的数据库单元测试辅助工具。

  60. Nick Wang
    *.*.*.*
    链接

    Nick Wang 2009-04-03 17:44:00

    我觉得从头到尾我都有点晕,不知道到底在讨论什么。你的问题到底是啥啊?看起来好像你都懂,没啥问题啊。

  61. rad
    *.*.*.*
    链接

    rad 2010-01-11 19:02:00

    弱弱的问下,最后一个代码里面
    public ActionResult Delete(string userName, Uri urlReferrer)

    是不是应该为
    public ActionResult Delete(string userName, [UrlReferrer]Uri urlReferrer)
    ?,我只有这样才可以...
    另外我这样做了以后,如果我在其他地方调用这个action的时候,例如
    <% html.ActionLink<HomeController>(c=>c.Delete(1),"Delete")%>
    是不是一定要写全两个参数啊 c=>c.Delete(1, null)这样?
    如何做到只传一个参数就可以呢?

  62. 老赵
    admin
    链接

    老赵 2010-01-13 12:14:00

  63. rad
    *.*.*.*
    链接

    rad 2010-01-13 16:35:00

    @Jeffrey Zhao
    没有没有,可能我没有说明白我的意思
    我的意思是
    如果有这么一个Action
    public ActionResult Delete(string userName, Uri urlReferrer)

    那么我在其他地方掉转到这个Action的时候,是不是一定要指定2个参数(userName, urlReferrer)呢?但是这里的url已经获取了呀,所以我想可不可以只指定一个参数?

  64. 老赵
    admin
    链接

    老赵 2010-01-13 16:50:00

    @rad
    你说明白了,我也理解了。

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我