这两天在构建RESTful服务的客户端,本以为已经一马平川了,没有想到就在下班时遇到一个问题。

我打算在服务器商把所处理的HTTP异常状态(400-599))状态一一返回给客户端组件,客户端收到各个状态码后根据号码及从服务器端返回的Description Content来抛出客户端的异常,结果在测试401时发现一个问题:在我的设想中,收到401,然后再去读取Response中的Status Description(我使用了自定义格式,详见上篇博文),结果出乎意料的是,服务器端发出了401,结果客户端没有抛出异常,而且按照接收成功的状态去了读取ATOM Feed的代码,而解析ATOM Feed时报错了,这让我很是诧异,经查看HttpStatusCode发现,返回的居然是200! RESTful的好处就是可以通过浏览器调用服务,把Url贴到浏览器中一看,原来返回的居然是LogOn视图...心中大概有数了,敢情MVC这家伙自做聪明,直接把401交给了Authentication Module去执行Unauthorized Handler了,整个过程类似在使用Authorize特性时用户没有登录一样(因为实际上在执行Authorization Filter时,也是通过HttpUnauthorizedResult实例将Response的StatusCode设置为401)。可是我现在使用的是客户端调用Web Service,你给我一个登录界面有啥用呢?再说了,我只是没有权限访问某个服务方法(Action),又不是没有登录,难道让我使用403去代替401么?但这不是我想要的,经过一番查找,发现在HttpApplication.EndRequest事件貌似可以,MSDN描述如下:“在 ASP.NET 响应请求时作为 HTTP 执行管线链中的最后一个事件发生”,最后一个事件应该是过了Authentication Module了吧,但是总觉得把这么一个事情放在Application中去处理,似乎有点太“重”了,凭什么其他的错误代码都可以作为ActionResult来处理,而一个401就能那么特殊呢,再说了,老衲一向痛恨“权贵”...再一找,又发现一个解决方案,HttpResponseBase.End 方法,MSDN描述如下:“在派生类中重写时,将当前缓冲的所有输出发送至客户端,停止执行请求的进程,并引发 EndRequest 事件”,换句话说,EndRequest就是End方法发出来了;而且在此之前“停止执行请求的进程”,这样Authentication Module就没有机会执行了,这样应该也不用去LogOn的Action了,哈哈,赶紧测试:

            this.HttpContext.Response.Clear();
            this.HttpContext.Response.StatusCode = 401;
            this.HttpContext.Response.End();
            return new EmptyResult();

先使用Chrome看下响应代码

401

果然,如愿得到了401,把代码做了些相应的改变,放入HttpStatusResult中,测试,客户端果然得到了401,完成。

最后再提供一句废话,听说把web.config中的 <authentication />配置节点去掉的话也能得到期望结果,如果您有兴趣可以试试(这不符合我的需求,所以我没有做测试)。

posted on 2011-03-31 23:09  think8848  阅读(3188)  评论(1编辑  收藏  举报