zoukankan      html  css  js  c++  java
  • ASP.NET Web API之消息[拦截]处理

    转载 http://www.cnblogs.com/newton/p/3238082.html

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

    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.

    如果要返回给客户端的不仅仅是一串字符串,比如是json对象,那么可以使用HttpError这个类。

    以上知识主要来自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。 

    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);
        }
    }
  • 相关阅读:
    【React Native】某个页面禁用物理返回键
    【React Native】DeviceEventEmitter监听通知及带参数传值
    转载【React Native代码】手写验证码倒计时组件
    【React Native】 中设置 APP 名称、应用图标、为安卓添加启动图
    【React Native错误集】* What went wrong: Execution failed for task ':app:installDebug'.
    【React Native错误集】Import fails with "Failed to execute 'ImportScripts' on 'WorkerGlobalScope'"
    【React Native错误集】Android error “Could not get BatchedBridge, make sure your bundle is packaged properly” on start of app
    「React Native笔记」在React的 setState 中操作数组和对象的多种方法(合集)
    【React Native】Error: Attribute application@allowBackup value=(false) from AndroidManifest.xml
    坚果云如何使用二次验证码/谷歌身份验证器/两步验证/虚拟MFA?
  • 原文地址:https://www.cnblogs.com/qtiger/p/10950234.html
Copyright © 2011-2022 走看看