32

asp.net core web api之异常

 3 years ago
source link: https://mp.weixin.qq.com/s?__biz=MzAwNTMxMzg1MA%3D%3D&%3Bmid=2654081282&%3Bidx=2&%3Bsn=18e328fe93d64002aae52c1cf17817c1
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

官方建议用app.UseExceptionHandler("/error")来集中处理异常,本例是一个具体的应用。

比如项目中有一个ViewModel,要求Name最大长度为5

    /// <summary>
/// 用户模型
/// </summary>
public class UserModel
{
/// <summary>
/// ID
/// </summary>
public int ID { get; set; }


/// <summary>
///名称
/// </summary>
[MaxLength(5, ErrorMessage = "长度不能超过5")]
public string Name { get; set; }
}

在TestController中有两个Action,都有异常的机率,Get方法中,一个异常是系统内置的0被整除,另一个是我们自定义的业务层级异常(.NET架构小技巧(8)中有涉及);AddUser是有Model验证有可能Name超过5个字符后报异常。Error方法一个错误处理Action,根据上下文的异常来分流系统内置异常,还是自定业务异常。

        /// <summary>
/// get接口
/// </summary>
/// <returns></returns>
[HttpGet]
public IActionResult Get()
{
var ran = new Random();
switch (ran.Next(1, 4))
{
case 1:
int i = 0;
var j = 10 / i;
return Ok();
case 2:
throw new RegisteredException("这是一个错误");
default:
return Ok();
}
}

/// <summary>
/// 添加用户接口
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
[HttpPost("/adduser")]
public IActionResult AddUser([FromBody] UserModel user)
{
return Ok(user);
}
/// <summary>
/// 错误处理页
/// </summary>
/// <returns></returns>
[HttpGet("/error")]
public IActionResult Error()
{
var context = HttpContext.Features.Get<IExceptionHandlerFeature>();
//如果是业务自定义异常,进行特殊处理
if (context.Error is DaMeiException)
{
return Problem(detail: context.Error.StackTrace, title: $"{context.Error.Message}", type: "HIS");
}
else
{
return Problem(detail: context.Error.StackTrace, title: context.Error.Message);
}
}

层级异常类

using System;
namespace WebApiError
{
/// <summary>
/// 产品异常
/// </summary>
public class DaMeiException : ApplicationException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public DaMeiException(string message) : base(message)
{
}
}
/// <summary>
/// His项目异常
/// </summary>
public class HisException : DaMeiException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public HisException(string message) : base(message)
{
}
}
/// <summary>
/// Lis项目异常
/// </summary>
public class LisException : DaMeiException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public LisException(string message) : base(message)
{
}
}
/// <summary>
/// 模块异常
/// </summary>
public class RegisteredException : HisException
{
/// <summary>
///
/// </summary>
/// <param name="message"></param>
public RegisteredException(string message) : base(message)
{
}
}
}

Error的Action之所有调用到,是因为Configure中添加如下代码,把所有异常交给"/error"来处理。

app.UseExceptionHandler("/error");

添加DaMeiProblemDetailsFactory来兼容各类异常,自定义异常和Model验证异常

using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.ModelBinding;
using System.Collections.Generic;


namespace WebApiError
{
/// <summary>
///
/// </summary>
public class DaMeiProblemDetailsFactory : ProblemDetailsFactory
{
/// <summary>
/// 处理业务错误
/// </summary>
/// <param name="httpContext"></param>
/// <param name="statusCode"></param>
/// <param name="title"></param>
/// <param name="type"></param>
/// <param name="detail"></param>
/// <param name="instance"></param>
/// <returns></returns>
public override ProblemDetails CreateProblemDetails(HttpContext httpContext, int? statusCode = null, string title = null, string type = null, string detail = null, string instance = null)
{
var problem = new ProblemDetails()
{
Title = string.IsNullOrEmpty(type) ? title : $"业务异常错误:{title}",
Detail = detail,
Status = statusCode,
Instance = instance,
Type = type
};
return problem;
}
/// <summary>
/// 处理model验证错误
/// </summary>
/// <param name="httpContext"></param>
/// <param name="modelStateDictionary"></param>
/// <param name="statusCode"></param>
/// <param name="title"></param>
/// <param name="type"></param>
/// <param name="detail"></param>
/// <param name="instance"></param>
/// <returns></returns>
public override ValidationProblemDetails CreateValidationProblemDetails(HttpContext httpContext, ModelStateDictionary modelStateDictionary, int? statusCode = null, string title = null, string type = null, string detail = null, string instance = null)
{
var problem = new ValidationProblemDetails()
{
Title = "Model验证错误",
Detail = detail,
Status = statusCode,
Instance = instance,
Type = type
};
foreach (var a in modelStateDictionary)
{
var errorList = new List<string>();
foreach (var error in a.Value.Errors)
{
errorList.Add(error.ErrorMessage);
}
problem.Errors.Add(new KeyValuePair<string, string[]>(a.Key, errorList.ToArray()));
}
return problem;
}
}
}

在ConfigureServices中需要注入DaMeiProblemDetailsFactory

  services.AddTransient<ProblemDetailsFactory, DaMeiProblemDetailsFactory>();

其实还可以用Action过滤器来统一管理异常

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;


namespace WebApiError
{
/// <summary>
/// 自定义过滤器处理异常
/// </summary>
public class DaMeiExceptionFilter : IActionFilter, IOrderedFilter
{
/// <summary>
///
/// </summary>
public int Order { get; } = int.MaxValue - 10;
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{


if (context?.Exception != null)
{
context.Result = new ObjectResult(context.Exception.Message)
{
StatusCode = 500
};
context.ExceptionHandled = true;
}
}
}
}

另外一种方式是通过Action过滤器来处理

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;


namespace WebApiError
{
/// <summary>
/// 自定义过滤器处理异常
/// </summary>
public class DaMeiExceptionFilter : IActionFilter, IOrderedFilter
{
/// <summary>
///
/// </summary>
public int Order { get; } = int.MaxValue - 10;
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuting(ActionExecutingContext context)
{
}
/// <summary>
///
/// </summary>
/// <param name="context"></param>
public void OnActionExecuted(ActionExecutedContext context)
{


if (context?.Exception != null)
{
if (context.Exception is DaMeiException)
{
context.Result = new ObjectResult(context.Exception.Message)
{
Value = $"业务异常:{ context.Exception.Message}",
StatusCode = 500
};
}
else
{
context.Result = new ObjectResult(context.Exception.Message)
{
Value = context.Exception.Message,
StatusCode = 500
};
}
context.ExceptionHandled = true;
}
}
}
}


添加全局过滤器

services.AddControllers(options =>
{
options.Filters.Add(new DaMeiExceptionFilter());
});

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK