2013-03-08 51 views
0

我對異常處理的一些問題在MVC 4處理Web API異常在MVC與處置

我已經實現HandleErrorAttribute和派生新的屬性來定製。它的工作非常好。但我不想每次都重定向用戶自定義錯誤頁面。

我在動作中遇到的一些錯誤,從Web API拋出,我想在當前頁面顯示給用戶。 例如,如果用戶想要創建記錄,但WebAPI由於模型狀態無效而引發異常,友好的方式顯示在創建視圖中的異常詳細信息。

但是HandleErrorAttribute默認重定向Error.cshtml。

我可以處理Actions中的每個異常,但我認爲可以有另一種方式。

而且我也跟着http://www.prideparrot.com/blog/archive/2012/5/exception_handling_in_asp_net_mvc實施HandleErrorAttribute

public class CustomHandleErrorAttribute : HandleErrorAttribute { 
     private readonly ILogger _logger; 

     public CustomHandleErrorAttribute() { 
      _logger = new NLogger(typeof(CustomHandleErrorAttribute)); 
     } 

     public override void OnException(ExceptionContext filterContext) { 
      if(filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled) { 
       return; 
      } 

      if(new HttpException(null, filterContext.Exception).GetHttpCode() != 500) { 
       return; 
      } 

      if(!ExceptionType.IsInstanceOfType(filterContext.Exception)) { 
       return; 
      } 

      // if the request is AJAX return JSON else view. 
      if(filterContext.HttpContext.Request.Headers["X-Requested-With"] == "XMLHttpRequest") { 
       filterContext.Result = new JsonResult { 
        JsonRequestBehavior = JsonRequestBehavior.AllowGet, 
        Data = new { 
         error = true, 
         message = filterContext.Exception.Message 
        } 
       }; 
      } 
      else { 
       var controllerName = (string)filterContext.RouteData.Values["controller"]; 
       var actionName = (string)filterContext.RouteData.Values["action"]; 
       var model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName); 
       filterContext.Result = new ViewResult { 

        ViewName = View, 
        MasterName = Master, 
        ViewData = new ViewDataDictionary<HandleErrorInfo>(model), 
        TempData = filterContext.Controller.TempData 
       }; 
      } 

      _logger.Error(filterContext.Exception.Message, filterContext.Exception); 

      filterContext.ExceptionHandled = true; 
      filterContext.HttpContext.Response.Clear(); 
      filterContext.HttpContext.Response.StatusCode = 500; 

      filterContext.HttpContext.Response.TrySkipIisCustomErrors = true; 
     } 
    } 

我通過的HttpClient和包裝類創建的Web API調用。例如,Get請求如下所示。

public async Task<BrandInfo> Create(BrandInfo entity) { 
      using(var apiResponse = await base.PostAsync(BaseUriTemplate, entity)) { 

       if(apiResponse.IsSuccess) { 
        return apiResponse.Model; 
       } 

       throw new HttpApiRequestException(
        string.Format(HttpRequestErrorFormat, (int)apiResponse.Response.StatusCode, apiResponse.Response.ReasonPhrase), 
        apiResponse.Response.StatusCode, apiResponse.HttpError); 
      } 
     } 
+0

重寫方法od並且不使用base.xx? – Rob 2013-03-08 13:24:39

+0

抱歉,我無法理解您提到的內容。 – 2013-03-08 13:29:25

+0

我假設你有這樣的事情:'public override onException',確保你沒有'base.onException(context);' – Rob 2013-03-08 13:31:47

回答

2

構建一個包裝HttpClient並使用它調用Web API的類。 在您希望重定向發生的情況下(即500 - 內部服務器錯誤或401 - 未授權),以及您希望顯示模型狀態錯誤的情況(400 - 錯誤的請求將是我的選擇),從Web API返回不同的HTTP狀態代碼。 。在您的包裝情況 手柄狀態代碼:

一)當你想重定向錯誤的情況下(500或從網絡API收到401),拋出合適的異常

b)如果你不想一個重定向(從Web API接收到400),只是從你的包裝類返回一些響應模型,可以顯示在客戶端

在你的控制器只是假設你將有HTTP包裝類返回的響應模型,因爲異常會導致它永遠不會回到控制器(您將在全局處理它並執行重定向)。

如果你需要一個代碼示例,我可以提供一個代碼示例,但我認爲你正在尋找一個更通用的概念,而不是具體的代碼。

編輯:

在網頁API方面:

public class ModelValidationFilterAttribute : ActionFilterAttribute 
{ 
    public override void OnActionExecuting(HttpActionContext actionContext) 
    { 
     if (!actionContext.ModelState.IsValid) 
     { 
      Dictionary<string,string> errors = new Dictionary<string, string>(); 
      foreach (KeyValuePair<string, ModelState> keyValue in actionContext.ModelState) 
      { 
       errors[keyValue.Key] = keyValue.Value.Errors.Select(e => e.ErrorMessage).FirstOrDefault(); 
      } 
      actionContext.Response = actionContext.Request.CreateResponse(HttpStatusCode.BadRequest, new ApiError(ApiErrorCode.ModelBindingError, errors)); 
     } 
    } 
} 

在您的全局。ASAX:

 GlobalConfiguration.Configuration.Filters.Add(new ModelValidationFilterAttribute()); 

定製ApiError中:

public class ApiError 
{ 
    public ApiErrorCode ErrorCode { get; set; } 
    public string ErrorMessage { get; set; } 
    public Dictionary<string, string> ModelStateErrors; 
} 

當涉及到MVC邊,這裏是你的包裝HttpClient的包裝類可能看起來怎麼樣:

public class RPCService 
{ 

    public async Task<RPCResponseModel<T>> GetAsync<T>(string controller, string action, Dictionary<string, string> queryParams, Dictionary<string, string> headers) 
    { 
     using (HttpClient client = new HttpClient()) 
     { 
      client.BaseAddress = new Uri("your host goes here"); 
      client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); /// Tell RPC to return data as json 
      if (headers != null) foreach (var header in headers) client.DefaultRequestHeaders.Add(header.Key, header.Value); 
      string query = Query(queryParams); 
       var response = await client.GetAsync(controller + "/" + action + query); 
       if (response.IsSuccessStatusCode) 
       { 
        return new RPCResponseModel<T> 
        { 
         StatusCode = response.StatusCode, 
         Data = await response.Content.ReadAsAsync<T>() 
        }; 
       } 
       else if(response.StatusCode == HttpStatusCode.BadRequest) 
       { 
        return new RPCResponseModel<T> 
        { 
         Error = await response.Content.ReadAsAsync<RPCErrorModel>(), 
         StatusCode = response.StatusCode 
        }; 
       } 
else 
{ 
    /// throw your exception to handle globally 
} 
     } 
    } 

與模式的反應會是:

public class RPCErrorModel 
{ 
    public int Code { get; set; } 
    public string Message { get; set; } 
    public Dictionary<string, string> ModelErrors; 
} 

public class RPCResponseModel 
{ 
    public RPCErrorModel Error { get; set; } 
    public HttpStatusCode StatusCode { get; set; } 
} 

public class RPCResponseModel<T> : RPCResponseModel 
{ 
    public T Data { get; set; } 
} 
+0

其實我的建築很適合你的建議。我編輯了我的問題並添加了一個代碼示例。我想拋出一個HttpApiRequestException並從控制器處理它。我怎樣才能做到這一點? – 2013-03-09 21:52:46

+0

我已經提供了一些上面的代碼。它可能不適用於複製+粘貼,但它是我剛剛寫的內容的實現。因此,您對MVC端的GetAsync方法的調用將返回包裝的響應,該響應攜帶狀態碼和錯誤或數據。在BadRequest或OK/NoContent的情況下會發生這種情況。如果您有錯誤,請從中提取模型狀態錯誤並將其返回給視圖,否則返回數據。如果您接收到除此之外的任何其他狀態碼,則拋出異常並將其捕獲到您的全局錯誤處理過濾器中/在某處執行重定向。 – 2013-03-09 22:11:20

+0

非常感謝,我現在開始工作 – 2013-03-09 22:12:13