您的位置:首页 > 编程语言 > ASP

为解决ASP.NET MVC(CTP)中URL“页面请求”和“单纯逻辑处理请求”混淆问题,提供一条思路

2008-01-07 21:17 771 查看
写在最前:本文主要是提供一种解决ASP.NET MVC(CTP)中URL“页面请求”和“单纯逻辑处理请求”混淆问题的思路,演示代码只作实现效果之用,不一定适合直接应用于“实战”,如有“粗燥”之处请多包涵。如果大家觉得可行,我们可以一起来完善她。
之前我很多次提到ASP.NET MVC 中“指令性”的URL,以及它可以给我们带来的一些新的体验,这样的URL可以把V层的页面逻辑(或者请求)让C层去承担,并且由C层负责判断到底将哪个网页最后传输到客户端。

这样的好处(或者说一部分的必要之处),是将V和C在一定程度上分离开来,一切以Controller为中心,而不再是aspx。但是这样“指令性”的URL我感觉更像是一把双刃剑,我们说他好,也可以说它有很大缺陷:

第一,比如当我们在一个Controller中的Action中的逻辑处理完毕之后,通常最后会有两种选择:RenderView(转向处理页面)或者RedirectToAction(转向另一个Action),如果你不这样做,而是像Web Forms或者ASP中那样下面不写点什么,那么返回的将是一个空白网页,至少不是你原来请求的入口网页。也就是说,一个Action只能返回相对固定的结果网页,除非你给他加上指定的参数或者每次都通过一系列判断。那如果我有很多地方要重用同一个Action,并且URL是不确定的,怎么办?

第二,由于MVC中请求的URL已经被Route过,并且通过一系列内部规则找到对应的Action才执行,当某个Action进行时,如果你想直接用Web Forms的方法得到请求网页的URL(Request.UrlReferrer),几乎是不可能的。这时候你用Request.URL找到的也不是你刚才在访问的网页URL,而是你主动请求的这个“指令性”URL。那么又出现一个问题,如果我想运行完一段Action后仍然回到这个网页,就像一个button被postback之后没有转向,那要如何做到呢?

基于这两点,我们当然可以有“水来土掩”的办法:

第一,不重用Action,而是为许多的Action创建重用的方法,Action之后到底RenderView/RedirectToAction到什么地方,还是由Action各自负责。

第二,每个这样的Action执行完之后,用最“死”的办法指定返回的页面(也就是说一个页面的逻辑处理只对应一个Action)。

这样确实可以解决两个问题,但是他们都有各自的很大的不足:

第一,这种方法使Action片断无畏的增加,这一切只是为了返回不同结果页而已。并且维护需要更加“深入”,不直观。同时这样做只会增加程序员体力和磁盘空间(说白了也就是“庞大”低效代码)的付出,不折不扣的“内耗”。

第二,同样的“内耗”。并且虽然没有涉及到较大的“重用”的需求,但是这些工作如果能像在Web Forms或者ASP中那样完成,岂不是更好。

为了同时解决这两个不足,我进行了几套方案的测试,并且最后确定了一套目前为止我所能想到的比较好的办法:把“指令性”URL“分流”,就是说指向同样的Controller中的同一个Action,通过一个页面上简单的参数,让他自动处理是返回请求页面还是继续。也就是说,把“页面请求”(不管是否需要逻辑处理,最后返回一个结果页)和“单纯逻辑请求”(就像我们很多时候用需要Web Forms中button做的那样)在需要的时候,彻底分离开,同时保证原有方法继续有效。

听上去似乎很容易实现,但是别忘了我有一些必要的前提:

1、能同时解决(或者改善)以上两个问题。

2、不破坏ASP.NET MVC本身的构架(也就是说不删除、修改任何代码,不Hack任何dll)。

3、尽量简单、高效,并且可能的话,考虑到改善其他安全性方面不足的问题。因为大家都知道,Web Forms的PostBack方法可以很好的解决一些安全性方面的问题(执行逻辑的触发上),而目前MVC的Action等于是全部暴露给用户的,既然要在URL和Action上动手脚,那么看看是否可以把这个问题顺带处理掉(因为这方面涉及的问题比较多,我的案例中只是提供一种可行的思路,并不着力实现它)

当然,还要说明一下,这个未必是最好的办法,我这里只是提供一个可行的思路,这是我在开发一个测试ASP.NET MVC的项目过程中临时碰到和想到的,只是“逢山开路”了一下,可能还有更好的方法没有想到,或者一些细微处没有兼顾到,欢迎大家指出。如果大家有更好的,或者是因为本人对ASP.NET MVC认识不足而导致的“多此一举”也欢迎探讨!

下面介绍一下我的思路的一种做法:

我们要同时解决URL和Action方面的问题,那么首先当然是要考虑到,如何获取到请求页的URL。既然Action不行,那么当然首先想到在Global.asax中指定(虽然这个办法有些“作孽”,不过因为只是测试,就先拿Global.asax开刀吧)。

我们在Global.asax中添加一套自己的URL标准格式:

RouteTable.Routes.Add(new Route

其中[ao]是Action Only的缩写,这个参数如果传入时不为0也不为空,那么就说明用户需要执行完后返回当前页,而不是View到其他地方去。

[aop]是Action Only Page的缩写,这个字段用户不用传入,有服务器自动获得当前的URL(确切的说,对服务器来说是上一个),我们此处用HttpContext.Current.Request.UrlReferrer.PathAndQuery获取路径和参数。之所以不让用户在页面就传入URL,也不依靠外部数据,我是考虑到两点:一、简单易用;二、安全性,这样对于可以触发某些Action Only的网页,我们可以在第一时间有完全的控制(逻辑还没有执行的时候),我们可以把aop的赋值过程写入一个单独的过程,筛选触发这个Action的网页的URL(如Admin目录下指定URL), 当然我这里为了方便测试,没有展开。这样从根本上杜绝了外部的注入(因为直接在浏览器中打开是没有UrlReferrer的)和内部自定义链接注入(就像cnblogs中,用户自己用编辑器发一个链接作为“指令”)。

接下来我们需要有一个方法来接收处理[ao][aop]指令。

我写了一个ActionOnlyForControllers.cs放入Controller文件夹,因为Controller后面加了s,所以你不用担心V层的ActionOnlyFor之类文件夹的干扰:

using System;

using System.Web;

using System.Web.Mvc;

namespace ActionOnlyForControllers.Controllers

过程上我都写了注释,大家如果发现有bug可以向我反映。

这个判断我们直接在Controller中调用,方法如下:

[ControllerAction]

public void About()

if (!ActionOnlyForControllers.CheckAO(RouteData))
可以实现“留在页面”或者“转向页面”的判断。

在HTML中我们只需要用一个ao参数来控制:ao=0或为空,则不执行判断,保持MVC原有形式。ao为其他值时,则执行完Action仍然回到本页面,其效果类似简单的postback一下。我们可以这样做:

<%= Html.ActionLink("执行方法后回到本页面(button效果)", new { ao = 1, action = "About", controller = "Home" })%><br />

<%= Html.ActionLink("执行方法后继续执行RenderView(hyperlink效果)", new { ao = 0, action = "About", controller = "Home" })%></p>

这样我们可以轻松的在“页面请求(+ 逻辑处理)”和“单纯逻辑处理”之间切换,让Action得到很好的重用,并且解决页面返回的问题。

当然,这些似乎看上去有点不太合ASP.NET MVC的本意,也许是我被Web Forms“宠”得太依赖于“postback体验”,但总之只要能为我们提供方便,并且达到特定的目的,”MVC”这个名字本身已经不再重要^_^

这里我提供了一个示例下载:ActionOnlyForControllers的一个简单示例

友情提示:其中使用的MVCToolkit.dll是我曾经修改过的:MVC Toolkit 部分已发现bug的根治方案 Part(1) ,请不要在不知情的情况下当作官方的dll引用到其他项目中,以免出错。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐