Web Api 之 404 Resource Not Found 错误消息封装

问题背景

最近在重构个以前做过的一个电商网站,做了很大的改动,主要是性能和稳定性。以后有机会详细讨论。

目前在基于Web Api开发API项目,供APP及移动站点使用。使用Asp.Net Web Api开发方便快捷毋庸多言,谁用谁知道,呵呵~~~

Web Api技术知道日久,基本也知道怎么回事,毕竟在使用上跟Asp.Net MVC使用比较相似。但是真正在开发的时候,还是大大小小碰到不少问题,也有挺多心得。如题问题即使其中的恶一个。如果时间足够,我将一一跟各位分享和讨论。针对某个问题,我的方案有错误或者您有更好的方案,也请不吝赐教。小弟在此谢过!!

问题

废话不多说,直接讨论本问题。在处理返回消息的时候,希望将返回的消息都进行统一。消息格式如:

response:
{
   head:{
   status: 200 //(200: ok, 404: 找不到方法/Action/controller, 500: 内部错误),
   errors: 错误信息
   }
   body: 具体返回数据
}

对于正常的Action返回的数据,有许多方式可以处理成我们所需要的格式,若有需要以后再开一贴单独讨论。但对于404错误,也就是找不到Action或者Controller的错误,就不是那么好处理。因为请求不会进入Action通道里,也就是Filter也够不到,无法拦截。

解决方案

首先想到的解决方案是在Application_Error事件里进行处理。但是通过实际的试验,发现此事件并不会触发,就算触发了,也没法将我们需要的标准数据格式返回到客户端来。

之后想到的方法,是在请求管道进行拦截处理。但是本人对WebApi研究不深,更何况是深奥的请求管道。在看了些资料和尝试之后,以失败告终。由于时间关系,只能放弃此方式。至于WebApi的请求管道,只能留待以后再细细研究。

无奈知晓,只能去网上继续查找看是否有现成方案。花费大量时间之后,依然没发现一个中文的解决方案。无奈只能求助于国外的资料。要说,还是国外资料全,没费多少时间,就找到一个方案。链接地址:http://weblogs.asp.net/imranbaloch/handling-http-404-error-in-asp-net-web-api

此方案的关键是继承ApiControllerActionSelector和DefaultHttpControllerSelector两个Selector类,尝试获取匹配请求的Controller和Action。若获取不到,则说明没有对应的Action和Controller。此时,将Action重定向到执行错误处理Action。请看具体实现代码。

1.尝试获取匹配请求的Controller和Action

public override HttpActionDescriptor SelectAction(HttpControllerContext controllerContext)
        {
            HttpActionDescriptor decriptor = null;
            try
            {
                decriptor = base.SelectAction(controllerContext);
            }
            catch (HttpResponseException ex)
            {
                var code = ex.Response.StatusCode;
                if (code != HttpStatusCode.NotFound && code != HttpStatusCode.MethodNotAllowed)
                    throw;
                var routeData = controllerContext.RouteData;
                routeData.Values["action"] = "Handle404";
                IHttpController httpController = new ErrorController();
                controllerContext.Controller = httpController;
                controllerContext.ControllerDescriptor = new HttpControllerDescriptor(controllerContext.Configuration, "Error", httpController.GetType());
                decriptor = base.SelectAction(controllerContext);
            }
            return decriptor;
        }
    }

    public class NotFoundControllerSelector : DefaultHttpControllerSelector
    {
        public NotFoundControllerSelector(HttpConfiguration configuration)
            : base(configuration)
        {
        }

        public override HttpControllerDescriptor SelectController(HttpRequestMessage request)
        {
            HttpControllerDescriptor decriptor = null;
            try
            {
                decriptor = base.SelectController(request);
            }
            catch (HttpResponseException ex)
            {
                var code = ex.Response.StatusCode;
                if (code != HttpStatusCode.NotFound)
                    throw;
                var routeValues = request.GetRouteData().Values;
                routeValues["controller"] = "Error";
                routeValues["action"] = "Handle404";
                decriptor = base.SelectController(request);
            }
            return decriptor;
        }
    }

2.在WebApiConfig中注册此二Selector

 config.Services.Replace(typeof (IHttpControllerSelector), new NotFoundControllerSelector(config));
 config.Services.Replace(typeof(IHttpActionSelector), new NotFoundActionSelector());

3.添加处理错误的Route,请务必将此路由注册在所有路由的最后。

config.Routes.MapHttpRoute(
                name: "Error404",
                routeTemplate: "{*url}",
                defaults: new { controller = "Error", action = "Handle404" }
            );

4.创建处理错误的Controller和Action。

public class ErrorController : ApiController
    {
        [HttpGet, HttpPost, HttpPut, HttpDelete, HttpHead, HttpOptions, AcceptVerbs("PATCH")]
        public ResultModel Handle404()
        {
            return new ResultModel() //ResultModel是本人创建的标准返回结果实体
            {
                head = new ResultHeaderModel() {status = HttpStatusCode.NotFound, errors = "Route not found"}
            };
        }
    }

5.测试程序,返回结果如预期。

本文到此结束,代码比较清晰,看即懂,恕不赘述。

若给位对本文有什么疑问或者建议,郁或者有其他方案,欢迎大家讨论。小弟在此谢过~~~

posted @ 2015-10-20 22:37  俗世人  阅读(7110)  评论(2编辑  收藏  举报