2013-03-08 20 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