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

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

标题至关难取,内容也许和您想的不同,并且网上已经有不少这方面的资料了,我不过是在实践过程当中做下记录。废话少说,直接开始。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);
    }
}
相关文章
相关标签/搜索