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);
    }
}
相關文章
相關標籤/搜索