Asp.net mvc 使用Handler在MVC上处理Web API异常
我对MVC4的异常处理有一些疑问 我已经实现了Asp.net mvc 使用Handler在MVC上处理Web API异常,asp.net-mvc,error-handling,asp.net-web-api,Asp.net Mvc,Error Handling,Asp.net Web Api,我对MVC4的异常处理有一些疑问 我已经实现了HandleErrorAttribute,并派生了一个新属性进行自定义。它工作得非常好;但我不想每次都重定向用户自定义错误页 我在操作中遇到的一些错误,从WebAPI抛出,我想在当前页面上向用户显示它们。 例如,如果用户想要创建一条记录,但WebAPI由于modelstate无效而引发异常,则在create视图中以友好的方式显示异常详细信息 但是默认情况下HandleErrorAttribute重定向Error.cshtml 我可以处理行动中的每一个
HandleErrorAttribute
,并派生了一个新属性进行自定义。它工作得非常好;但我不想每次都重定向用户自定义错误页
我在操作中遇到的一些错误,从WebAPI抛出,我想在当前页面上向用户显示它们。
例如,如果用户想要创建一条记录,但WebAPI由于modelstate无效而引发异常,则在create视图中以友好的方式显示异常详细信息
但是默认情况下HandleErrorAttribute重定向Error.cshtml
我可以处理行动中的每一个异常,但我认为还有另一种方法
我还遵循了实现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;
}
}
<代码>公共类CustomHandleErrorAttribute:HandleErrorAttribute{
专用只读ILogger\u记录器;
公共CustomHandleErrorAttribute(){
_记录器=新的NLogger(类型(CustomHandleErrorAttribute));
}
公共覆盖无效OneException(例外上下文筛选器上下文){
if(filterContext.ExceptionHandled | | |!filterContext.HttpContext.IsCustomErrorEnabled){
返回;
}
if(新的HttpException(null,filterContext.Exception).GetHttpCode()!=500){
返回;
}
如果(!ExceptionType.IsInstanceOfType(filterContext.Exception)){
返回;
}
//如果请求是AJAX,则返回JSON else视图。
if(filterContext.HttpContext.Request.Headers[“X-Requested-With”]=“XMLHttpRequest”){
filterContext.Result=新的JsonResult{
JsonRequestBehavior=JsonRequestBehavior.AllowGet,
数据=新{
错误=真,
message=filterContext.Exception.message
}
};
}
否则{
var controllerName=(字符串)filterContext.RouteData.Values[“controller”];
var actionName=(字符串)filterContext.RouteData.Values[“action”];
var model=newhandleerrorinfo(filterContext.Exception、controllerName、actionName);
filterContext.Result=新的ViewResult{
ViewName=视图,
MasterName=Master,
ViewData=新的ViewDataDictionary(型号),
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);
}
}
公共异步任务创建(BrandInfo实体){
使用(var apiResponse=wait base.PostAsync(BaseUriTemplate,entity)){
if(apiResponse.issueccess){
返回apiResponse.Model;
}
抛出新的HttpApiRequestException(
格式(HttpRequestErrorFormat,(int)apiResponse.Response.StatusCode,apiResponse.Response.ReasonPhase),
apiResponse.Response.StatusCode,apiResponse.HttpError);
}
}
构建一个类,该类将封装HttpClient并使用它调用Web API。
从Web API返回不同的HTTP状态代码,用于希望发生重定向的情况(即500-内部服务器错误,或401-未经授权)和希望显示模型状态错误的情况(400-错误请求将是我的选择)。
将包装盒中的状态代码处理为:
a) 如果希望重定向(从Web API接收到500或401)时出错,则引发相应的异常
b) 当您不需要重定向(从WebAPI接收400)时,只需从包装器类返回一些响应模型,这些模型可以显示在客户端
在您的控制器中,只需假设您将从HTTP包装器类返回响应模型,因为异常将导致它永远无法返回控制器(您将全局处理它并执行重定向)
如果您需要一个代码示例,我可以提供一个,但我认为您更多的是寻找一个通用的概念,而不是具体的代码
编辑:
在Web 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));
}
}
}
自定义API错误:
public class ApiError
{
public ApiErrorCode ErrorCode { get; set; }
public string ErrorMessage { get; set; }
public Dictionary<string, string> ModelStateErrors;
}
public类错误
{
公共ApiErrorCode错误代码{get;set;}
公共字符串错误消息{get;set;}
公共字典错误;
}
说到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
}
}
}
公共类RPCService
{
公共异步任务GetAsync(字符串控制器、字符串操作、字典查询参数、字典头)
{
使用(HttpClient=new HttpClient())
{
client.BaseAddress=新Uri(“您的主机转到此处”);
client.DefaultRequestHeaders.Accept.Add(新的MediaTypeWithQualityHeaderValue(“应用程序/json”);///告诉RPC以json的形式返回数据
if(headers!=null)foreach(headers中的var header)client.DefaultRequestHeaders.Add(header.Key,header.Value);
字符串查询=查询(查询参数);
var response=wait client.GetAsync(控制器+“/”+操作+查询);
if(响应。IsSuccessStatusCode)
{
返回新的RPCresponsModel
{
StatusCode=response.StatusCode,
Data=wait response.Content.ReadAsAsync()
};
}
else if(response.StatusCode==HttpStatusCode.BadRequest)
{
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; }
}