前言
閱讀本文之前,您也可以到Asp.Net Web API 2 系列導航進行查看 http://www.cnblogs.com/aehyok/p/3446289.html
本文主要來講解Asp.Net Web API中錯誤和異常的處理,包括以下幾點:
1.HttpResponseException——HTTP響應異常
2.Exception Filters——異常過濾器
3.Registering Exception Filters——注冊異常過濾器
4.HttpError——HTTP錯誤
HttpResponseException——HTTP響應異常
如果一個Web API 控制器拋出一個未捕獲的異常,會發生什么?在默認情況下,大多數異常都被轉換為一個帶有狀態碼500的內部服務器錯誤的HTTP響應。
這個HTTPResponseException類型是一個特殊的類型。這種異常會返回你在異常構造器中指定的任何HTTP狀態碼。例如,在以下方法中,如果這個id參數無效,那么會返回“404——未找到”。
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; }
為了對響應進行更多的控制,你也可以構造整個響應消息,並用HTTPResponseException來包含它:
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var resp = new HttpResponseMessage(HttpStatusCode.NotFound) { Content = new StringContent(string.Format("No product with ID = {0}", id)), ReasonPhrase = "Product ID Not Found" } throw new HttpResponseException(resp); } return item;
Exception Filters——異常過濾器
通過編寫一個異常過濾器,你可以定制Web API如何處理異常。當一個控制器拋出一個未處理的異常,且這個異常不是一個HttpResponseException異常時,一個異常過濾器會被執行。HttpResponseException類型一個特殊的情況,因為它是專門設計用來返回一個HTTP響應的。
異常過濾器實現System.Web.Http.Filters.IExceptionFilter接口。編寫異常過濾器最簡單的方式是通過System.Web.Http.Filters.ExceptionFilterAttribute類進行派生,並重寫其OnException方法。
ASP.NET Web API中的異常過濾器與Asp.Net MVC中的是極為類似的。然后,他們被聲明在不同的命名空間中,且功能也是獨立的。特別強調一下,Asp.Net MVC中使用的HandleErrorAttribute類不會處理Web API控制器中拋出的異常。
以下是將NotImplementedException異常轉換成HTTP狀態碼“501 — 未實現”的一個過濾器:
using System; using System.Net; using System.Net.Http; using System.Web.Http.Filters; public class NotImplExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext context) { if (context.Exception is NotImplementedException) { context.Response = new HttpResponseMessage(HttpStatusCode.NotImplemented); } } }
HttpActionExecutedContext對象的Response屬性含有將發送給客戶端的HTTP響應消息。
Registering Exception Filters——注冊異常過濾器
以下是注冊Web API異常過濾器的幾種方式:
- 通過動作進行注冊
- 通過控制器進行注冊
- 全局注冊
要把過濾應用於特定的動作,在動作上添加該過濾器的注解屬性:
public class ProductsController : ApiController { [NotImplExceptionFilter] public Contact GetContact(int id) { throw new NotImplementedException("This method is not implemented"); } }
要把過濾器運用於一個控制器的所有動作,在控制器上添加該過濾器的注解屬性:
[NotImplExceptionFilter] public class ProductsController : ApiController { // ... }
要全局性地把過濾器運用於所有Web API控制器,將該過濾器的一個實例添加到GlobalConfiguration.Configuration.Filters集合。這個集合中的異常過濾器會運用於任何Web API控制器動作。
GlobalConfiguration.Configuration.Filters.Add( new ProductStore.NotImplExceptionFilterAttribute());
如果你用的是“ASP.NET MVC 4 Web應用程序”項目模板創建的項目,要把你的Web API配置代碼被放在WebApiConfig類中,它位於App_Start文件夾:
public static class WebApiConfig { public static void Register(HttpConfiguration config) { config.Filters.Add(new ProductStore.NotImplExceptionFilterAttribute()); // Other configuration code(其它配置代碼)... } }
HttpError——HTTP錯誤
HttpError對象為在響應正文中返回錯誤消息提供了相應的方式。以下示例演示了如何用HttpError在響應體中返回HTTP狀態碼“404 — 未找到”:
public HttpResponseMessage GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var message = string.Format("Product with id = {0} not found", id); HttpError err = new HttpError(message); return Request.CreateResponse(HttpStatusCode.NotFound, err); } else { return Request.CreateResponse(HttpStatusCode.OK, item); } }
在這個例子中,如果該方法成功,它會在HTTP響應中返回產品。但如果所請求的產品未找到,則HTTP響應會在請求體中包含一個HttpError。該響應看上去大致像這樣:
HTTP/1.1 404 Not Found Content-Type: application/json; charset=utf-8 Date: Thu, 09 Aug 2012 23:27:18 GMT Content-Length: 51 { "Message": "Product with id = 12 not found" }
注意,在這個例子中,HttpError會被序列化成JSON。使用HttpError的一個好處是,與其它強類型模型一樣,會進行同樣的“content-negotiation”(暫未實現)和序列化過程。
直接替代創建HttpError對象的一種辦法是,你可以使用CreateErrorResponse方法:
public HttpResponseMessage GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var message = string.Format("Product with id = {0} not found", id); return Request.CreateErrorResponse(HttpStatusCode.NotFound, message); } else { return Request.CreateResponse(HttpStatusCode.OK, item); } }
CreateErrorResponse在System.Net.Http.HttpRequestMessageExtensions類中被定義為一個擴展方法。本質上,CreateErrorResponse會創建一個HttpError實例,然后創建一個包含該HttpError的HttpResponseMessage。
Adding Custom Key-Values to HttpError把自定義的鍵值添加到HTTPError
HttpError類實際上是一個“鍵-值”集合(它派生於Dictionary<string, object>),因此你可以添加自己的“鍵-值”對:
public HttpResponseMessage GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var message = string.Format("Product with id = {0} not found", id); var err = new HttpError(message); err["error_sub_code"] = 42; return Request.CreateErrorResponse(HttpStatusCode.NotFound, err); } else { return Request.CreateResponse(HttpStatusCode.OK, item); } }
Using HttpError with HttpResponseException以HttpResponseException的方式來使用HttpError
前面的例子是從控制器動作返回一個HttpResponseMessage消息,但你也可以使用HttpResponseException來返回一個HttpError。這讓你能夠在正常成功情況下返回強類型模型,而在有錯誤時,仍返回HttpError。
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { var message = string.Format("Product with id = {0} not found", id); throw new HttpResponseException( Request.CreateErrorResponse(HttpStatusCode.NotFound, message)); } else { return item; } }
總結
感覺比MVC中的異常處理更為出色,不知道新版本的MVC中的異常處理機制如何。下一篇文章將來講解Web API2中新增加的一個亮點機制————屬性路由,貌似很牛逼的樣子。