问题背景
最近在重构个以前做过的一个电商网站,做了很大的改动,主要是性能和稳定性。以后有机会详细讨论。
目前在基于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.测试程序,返回结果如预期。
本文到此结束,代码比较清晰,看即懂,恕不赘述。
若给位对本文有什么疑问或者建议,郁或者有其他方案,欢迎大家讨论。小弟在此谢过~~~