转载 http://www.cnblogs.com/newton/p/3238082.html、html
标题至关难取,内容也许和您想的不同,并且网上已经有不少这方面的资料了,我不过是在实践过程当中做下记录。废话少说,直接开始。web
Exceptionjson
当服务端抛出未处理异常时,most exceptions are translated into an HTTP response with status code 500, Internal Server Error.固然咱们也能够抛出一个特殊的异常HttpResponseException,它将被直接写入响应流,而不会被转成500。api
public Product GetProduct(int id) { Product item = repository.Get(id); if (item == null) { throw new HttpResponseException(HttpStatusCode.NotFound); } return item; }
有时要对服务端异常作一封装,以便对客户端隐藏具体细节,或者统一格式,那么可建立一继承自System.Web.Http.Filters.ExceptionFilterAttribute的特性,以下:asp.net
public class APIExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext context) { //业务异常 if (context.Exception is BusinessException) { context.Response = new HttpResponseMessage { StatusCode = System.Net.HttpStatusCode.ExpectationFailed }; BusinessException exception = (BusinessException)context.Exception; context.Response.Headers.Add("BusinessExceptionCode", exception.Code); context.Response.Headers.Add("BusinessExceptionMessage", exception.Message); } //其它异常 else { context.Response = new HttpResponseMessage { StatusCode = System.Net.HttpStatusCode.InternalServerError }; } } }
而后将该Attribute应用到action或controller,或者GlobalConfiguration.Configuration.Filters.Add(new APIExceptionFilterAttribute());使之应用于全部action(If you use the "ASP.NET MVC 4 Web Application" project template to create your project, put your Web API configuration code inside the WebApiConfig
class, which is located in the App_Start folder:config.Filters.Add(newProductStore.NotImplExceptionFilterAttribute());)。固然,在上述代码中,咱们也能够在OnException方法中直接抛出HttpResponseException,效果是同样的。ide
Note: Something to have in mind is that the ExceptionFilterAttribute will be ignored if the ApiController action method throws a HttpResponseException;If something goes wrong in the ExceptionFilterAttribute and an exception is thrown that is not of type HttpResponseException, a formatted exception will be thrown with stack trace etc to the client.spa
若是要返回给客户端的不单单是一串字符串,好比是json对象,那么可使用HttpError这个类。.net
以上知识主要来自Exception Handling in ASP.NET Web API。日志
ActionFilterAttribute、ApiControllerActionInvoker code
有时要在action执行先后作额外处理,那么ActionFilterAttribute和ApiControllerActionInvoker就派上用场了。好比客户端请求发过来的参数为用户令牌字符串token,咱们要在action执行以前先将其转为action参数列表中对应的用户编号ID,以下:
public class TokenProjectorAttribute : ActionFilterAttribute { private string _userid = "userid"; public string UserID { get { return _userid; } set { _userid = value; } } public override void OnActionExecuting(HttpActionContext actionContext) { if (!actionContext.ActionArguments.ContainsKey(UserID)) { //参数列表中不存在userid,写入日志 //…… var response = new HttpResponseMessage(); response.Content = new StringContent("用户信息转换异常."); response.StatusCode = HttpStatusCode.Conflict; //在这里为了避免继续走流程,要throw出来,才会立马返回到客户端 throw new HttpResponseException(response); } //userid系统赋值 actionContext.ActionArguments[UserID] = actionContext.Request.Properties["shumi_userid"]; base.OnActionExecuting(actionContext); } public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext) { base.OnActionExecuted(actionExecutedContext); } }
ActionFilterAttribute如何应用到action,和前面的ExceptionFilterAttribute相似。
ApiControllerActionInvoker以上述Exception为例:
public class ServerAPIControllerActionInvoker : ApiControllerActionInvoker { public override Task<HttpResponseMessage> InvokeActionAsync(HttpActionContext actionContext, CancellationToken cancellationToken) { //对actionContext作一些预处理 //…… var result = base.InvokeActionAsync(actionContext, cancellationToken); if (result.Exception != null && result.Exception.GetBaseException() != null) { var baseException = result.Exception.GetBaseException(); if (baseException is BusinessException) { return Task.Run<HttpResponseMessage>(() => { var response = new HttpResponseMessage(HttpStatusCode.ExpectationFailed); BusinessException exception = (BusinessException)baseException; response.Headers.Add("BusinessExceptionCode", exception.Code); response.Headers.Add("BusinessExceptionMessage", exception.Message); return response; }); } else { return Task.Run<HttpResponseMessage>(() => new HttpResponseMessage(HttpStatusCode.InternalServerError)); } } return result; } }
而后注册至GlobalConfiguration.Configuration.Services中。因为ApiControllerActionInvoker乃是影响全局的,因此若要对部分action进行包装处理,应该优先选择ActionFilterAttribute。
DelegatingHandler
前面的拦截都发生在请求已被路由至对应的action后发生,有一些状况须要在路由以前就作预先处理,或是在响应流返回过程当中作后续处理,这时咱们就要用到DelegatingHandler。好比对请求方的身份验证,当验证未经过时直接返回错误信息,不然进行后续调用。
public class AuthorizeHandler : DelegatingHandler { private static IAuthorizer _authorizer = null; static AuthorizeHandler() { } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (request.Method == HttpMethod.Post) { var querystring = HttpUtility.ParseQueryString(request.RequestUri.Query); var formdata = request.Content.ReadAsFormDataAsync().Result; if (querystring.AllKeys.Intersect(formdata.AllKeys).Count() > 0) { return SendError("请求参数有重复.", HttpStatusCode.BadRequest); } } //请求方身份验证 AuthResult result = _authorizer.AuthRequest(request); if (!result.Flag) { return SendError(result.Message, HttpStatusCode.Unauthorized); } request.Properties.Add("shumi_userid", result.UserID); return base.SendAsync(request, cancellationToken); } private Task<HttpResponseMessage> SendError(string error, HttpStatusCode code) { var response = new HttpResponseMessage(); response.Content = new StringContent(error); response.StatusCode = code; return Task<HttpResponseMessage>.Factory.StartNew(() => response); } }