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

ASP.NET MVC 3.0:基于Ajax的表单提交,A页面认证失败后页面被强转至登录页面,待登录成功将如何回到A页面?

2012-01-11 12:59 1056 查看
======================================================

注:本文源代码点此下载

======================================================

很多网站的首页都提供信息的输入,而不论您是否有账户且已登录。比如我喜欢逛的42qu(我跟创始人无任何关系,仅是喜欢该网站且无意广告,有兴趣的可以瞧瞧去)。当我发表自己的"碎碎念"时,会被自动跳转到登录页,而问题是登录成功后能否再回到原来的页面。听起来这个问题略显乏善可陈,然而它的实现框架是mvc
3.0,而且为了寻求其优雅的实现方式尝试了很多天,现作为记要并分享一下。

先来一张最终效果图吧:




上图的页面切分为两个区域,一是碎碎念的发表区(上部分)、另一个是呈现区(下部分),以真分页提取数据;信息提交是基于ajax.beginform()进行的,然后即时无刷新于呈现区。

看看这个基于异步提交的表单是如何陈述的:



值得关注的属性有updatetargetid,它预示着异步加载的数据将呈现于哪个div;另外onsuccess事件也尤为重要,它担当着回调的处理重任。最后,异步请求的数据想成功加载于指定的div中,得引用 jquery.validate.unobtrusive.min.js脚本文件(第3行):



这个js文件的功能类似于asp.net ajax中的"万能"控件updatepanel。
现在我想发表一条碎碎念,试图点击"碎碎念"进行发送,由于近一个星期没有登录,其相关cookie已然过期,页面将被强转至登录页(login.cshtml),跳转本身倒是我的设计意图,但页面呈现出的结果却很糟糕,因为整个登录页面全部呈现在id名为"chatintegrationcontent"的div内,其页面大家可以想像得出是很荒唐的,于是我要解决两个问题:
1.默认的认证属性authorizeattribute已经不能满足我的认证需求了,我得自定义认证类loginauthorizeattribute并继承它,且重写(override)相关受保护的虚方法(protected virtual method);
2.服务器端的任何跳转显然已是徒劳:无论是redirect还是变向的模拟server.transfer模式来曲线跳转,都会乖乖地落入原updatetargetid属性指定的div中。一开始我想,在服务端我能取消ajax调用吗?使其转变成普通调用,即形同@html.beginform()这种普通表单的post请求。答案是no!这个请求是ajax异步产生的,其命运已然注定,故唯一的途径便是通知客户端脚本作window.location跳转!
祸源于ajax异步调用,解决之道自然还要从它入手,且看看自定义认证类有如何了不得:



继承authorizeattribute后,仅重写了虚函数:protected virtual void handleunauthorizedrequest(authorizationcontext filtercontext);这意味着形如:[authorize(roles = "member")]这样的认证逻辑依旧采用mvc 3.0自己的算法,看了一下它的源码,确实比我自己另起灶炉写得全面而优雅得多,我羞愧的将自己实现的百来行代码统统删除,面向对象的可重用性真是棒啊。可以看到,上下文对象filtercontext的属性result已被设置成为jsonresult。另外有个内在原因要注意,authorize属性(attribute)认证失败后,是因为抛出了401异常,才导致mvc框架跳转到登录页(loginurl属性所指定):



loginurl属性显式指定了登录页,按照dudu老大的建议,cookieless也显式指定为 usecookies,为测试方便cookie和认证票据(ticket)都只设置一分钟(timeout="1")。

上述代码最重要的一句是:filtercontext.httpcontext.request.isajaxrequest()
它拷问当前请求是否为异步的ajax请求,之所以这么判断是因为客户端发来的假如是基于@html.beginform()的普通请求,那服务端可以轻松跳转而刷新整个浏览器,不用担心登录页落入相关div之内;相反ajax请求则要加倍呵护,它得通知js脚本作客户端跳转(window.location),方能保证登录页是刷新整个浏览器。以json格式封装一个键值对传回客户端:



继承自actionresult的"json格式内容"类jsonresult,有一个专门用来装载数据的object型属性data,目前只需要一个键值对足够。另外jsonrequestbehavior = jsonrequestbehavior.allowget也举足轻重:为了避免json hijacking攻击,自mvc2.0起任何以jsonresult类返回的请求都不允许get型的ajax请求获取数据,即便数据对象内才一个键值对,而妥协地采用post请求也将使数据无法被浏览器缓存,其实这些数据的敏感度又不高,所以采用get请求是个很好的开发体验!到目前为止,对客户端发来的ajax请求,服务端在认证失败情况下给出了应答,即通知客户端脚本"我对你验证失败"----ajax_unauthorized。
还记得ajax.beginform()里的属性onsuccess吧?现在看看它的js函数:



客户端的接收也很干脆,如果通知信息两相统一,那么直接赋值给window.location作页面跳转,刷新整个浏览器。采用request.url获取到当前页面的路由网址作为跳转路径的参数一并携带过去,结果形如: http://localhost:1650/account/logon?returnurl=http%3a%2f%2flocalhost%3a1650%2f 强转到登录页时,得先让用户输入表单,待提交表单且登录验证通过后,才利用returnurl参数值跳转回原来的页面,所以我们需要把returnurl参数值暂存起来,看看logon.cshtml的登录表单头:



beginform()的第三位参数叫路由参数:object routevalues,我们把"返回页"暂存于此,待将来登录表单提交时,该参数将一并提交到服务器,交由标记了httppost特性的action处理:



路由网址中的参数returnurl可以在 httppost限定的action作为参数获取:显然第一个是登录表单实体,第二个便是其参数。
只要判断传入参数returnurl是否为空,便可作出恰当的跳转选择,所以只要returnurl是一个具体的网址就可以顺利返回,这就是mvc3.0中对返回原页面的较为简洁而优雅的处理方案。

======================================================

在最后,我邀请大家参加新浪APP,就是新浪免费送大家的一个空间,支持PHP+MySql,免费二级域名,免费域名绑定
这个是我邀请的地址,您通过这个链接注册即为我的好友,并获赠云豆500个,价值5元哦!短网址是http://t.cn/SXOiLh我创建的小站每天访客已经达到2000+了,每天挂广告赚50+元哦,呵呵,饭钱不愁了,\(^o^)/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: