一、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); } };