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