标題相當難取,内容也許和您想的不一樣,而且網上已經有很多這方面的資料了,我不過是在實踐過程中作下記錄。廢話少說,直接開始。
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作為消息處理器。
參考資料:
- ASP.NET Web API Exception Handling
- Implementing message handlers to track your ASP.NET Web API usage
- MVC4 WebAPI(二)——Web API工作方式
- Asp.Net MVC及Web API架構配置會碰到的幾個問題及解決方案
- HTTP Message Handlers in ASP.NET Web API
轉載請注明原文出處:http://www.cnblogs.com/newton/p/3238082.html