• ASP.NET Web API之消息[拦截]处理


    标题相当难取,内容也许和您想的不一样,而且网上已经有很多这方面的资料了,我不过是在实践过程中作下记录。废话少说,直接开始。

    Exception


    当服务端抛出未处理异常时,most exceptions are translated into an HTTP response with status code 500, Internal Server Error.当然我们也可以抛出一个特殊的异常HttpResponseException,它将被直接写入响应流,而不会被转成500。

    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的特性,如下:

    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,效果是一样的。

    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.

    .net还内置了HttpError这个类,若想返回格式化对象(如json、xml等),用起来更方便。The HttpError class is actually a key-value collection (it derives from Dictionary<string, object>), so you can add your own key-value pairs.

    以上知识主要来自Exception Handling in ASP.NET Web API

    ActionFilterAttribute、ApiControllerActionInvoker 


    有时要在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。另外ApiControllerActionInvoker在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);
        }
    }

    这里的DelegatingHandler用于服务端,其实DelegatingHandler也可以在发起调用时使用,HttpClient可接收一个DelegatingHandler作为消息处理器。

    参考资料:

    转载请注明原文出处:http://www.cnblogs.com/newton/p/3238082.html

  • 相关阅读:
    取得当前目录下所有文件名
    windows用户态和内核态
    MFC CDialog中控件跨线程访问失败
    Effective C++学习笔记(八)
    Effective C++学习笔记(六)
    Effective C++学习笔记(七)
    Effective C++学习笔记(五)
    Effective C++学习笔记(四)
    Effective C++学习笔记(三)
    Effective C++学习笔记(二)
  • 原文地址:https://www.cnblogs.com/newton/p/3238082.html
Copyright © 2020-2023  润新知