一、HttpResponseException
如果一個Web API控制器拋出一個未捕捉異常,默認地,大多數異常都會被轉化成一個帶有狀態碼“500 – 內部服務器錯誤”的HTTP響應。HttpResponseException(HTTP響應異常)類型會返回你在異常構造器中指定的任何HTTP狀態碼。例如,在以下方法中,如果id參數非法,會返回“404 — 未找到”。
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) {
//指定響應狀態碼 throw new HttpResponseException(HttpStatusCode.NotFound); } return item; }
為了對響應進行更多控制,你也可以構造整個響應消息HttpResponseMessage,並用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" }
//包含一個HttpResponseMessage throw new HttpResponseException(resp); } return item; }
二、Exception Filters
繼承ExceptionFilterAttribute,重寫OnException,最后都是拋出HttpResponseException,包含一個HttpResponseMessage,調用客戶端可以獲取該異常信息,進行相應處理。
public class ExceptionHandlingAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext context) { if (context.Exception != null && !(context.Exception is HttpResponseException)) { MetricsConfig.MarkException(context.Exception); if (context.Exception is UnauthorizedAccessException) { throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.Unauthorized)); } var exceptionData = new ExceptionData { Name = context.Exception.GetType().Name, Message = context.Exception.GetBaseException().Message }; if (context.Exception is ApplicationException) { exceptionData.Message = context.Exception.Message; var businessException = context.Exception as CustomBusinessException; if (businessException != null) { exceptionData.Data = businessException.Data; } } throw new HttpResponseException(new HttpResponseMessage(HttpStatusCode.InternalServerError) { Content = new ObjectContent<ExceptionData>(exceptionData, JsonFormatter), ReasonPhrase = context.Exception.GetType().Name }); } } private JsonMediaTypeFormatter _JsonFormatter; private JsonMediaTypeFormatter JsonFormatter { get { if (_JsonFormatter == null) { _JsonFormatter = new JsonMediaTypeFormatter(); _JsonFormatter.SerializerSettings.ContractResolver = new CamelCasePropertyNamesContractResolver(); } return _JsonFormatter; } } // A simple class for generate response with json content. public class ExceptionData { public string Name { get; set; } public string Message { get; set; } public string Data { get; set; } } }
注冊:
以下是全局注冊,當然也可以在Controller或Action上標注
public static class WebApiConfig { public static void Register(HttpConfiguration config) { // register exception handler. config.Filters.Add(new ExceptionHandlingAttribute()); } }
三、客戶端獲取異常
通過AngularJs中AOP攔截響應實現
// apiInterceptor is responsible to handle the aspect of each request and response. webservices.factory('apiInterceptor', ['$q', '$log', '$injector', 'loginContext', 'eventAggregator', function ($q, $log, $injector, loginContext, eventAggregator) { 'use strict'; var apiToken = loginContext.apiToken; var tokenType = loginContext.tokenType; var webApiHostUrl = loginContext.apiHost + "/api/v1"; return { //token save to services for further usage tokenType: tokenType, apiToken: apiToken, webApiHostUrl: webApiHostUrl, // On request success 請求攔截 request: function (config) { if (config.isWebApiRequest && !config.isPlugin) {
//地址上都自動附加上/api/v1 config.url = (config.mkApiUrl || webApiHostUrl) + config.url; config.headers = config.headers || {};
//添加Authorization,用tokeentype token格式來定義 ,如 ‘bearer sfsfsfsfsdf=sfsf+...’
config.headers.Authorization = tokenType + ' ' + (config.mkToken || apiToken); var specificOfficeId = Ares.specificOfficeUtil.getOfficeId(); if (specificOfficeId) { config.headers["specific-office-id"] = specificOfficeId; } } else if (config.handleApiRequest) { config = config.handleApiRequest(config); } return config; }, // On request failure requestError: function (rejection) { $log.error(rejection); // Contains the data about the error on the request. // Return the promise rejection. return $q.reject(rejection); }, // On response failture,響應攔截 responseError: function (response) { $log.error(response); // Contains the data about the error. if (response.status === 401) {//狀態碼判斷 //window.location = '/logoff'; Ares.logOff(); } else if (response.data) {//返回內容判斷 if (response.data.name == 'TenantInactiveException') { aresMaintainUtil.goToTenantInactivePage(); }
//發布一個訂閱,導致彈出一個對話框 eventAggregator.publish(eventAggregator.events.ApiErrorHappened, response, 'apiInterceptor'); } else if (response.status === 0) { var isSaasApi = true; if (response.config && response.config.url.indexOf('//marketcenter') > -1) { isSaasApi = false; } if (isSaasApi) { aresMaintainUtil.ensureInMaintainMode().then(function (isInMaintainMode) { if (isInMaintainMode) { aresMaintainUtil.goToMaintainPage(); } }); } } // Return the promise rejection. return $q.reject(response); } }; }]); //Aop攔截,對響應 webservices.config(['$httpProvider', function ($httpProvider) { $httpProvider.interceptors.push('apiInterceptor'); }]);
注冊訂閱:
var subscribeEvents = function () { eventAggregator.subscribe($scope, eventAggregator.events.ApiErrorHappened, onApiErrorHappened); }; //彈出提示對話框 var onApiErrorHappened = function (event, args) { if (args.data.name == 'MyOperationException' || args.data.name == 'CustomBusinessException') { customDialog.info('系統提示', args.data.message); } };