問題背景
最近在重構個以前做過的一個電商網站,做了很大的改動,主要是性能和穩定性。以后有機會詳細討論。
目前在基於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.測試程序,返回結果如預期。
本文到此結束,代碼比較清晰,看即懂,恕不贅述。
若給位對本文有什么疑問或者建議,郁或者有其他方案,歡迎大家討論。小弟在此謝過~~~