2.接口输入校验、输出格式、及异常处理——SnailAspNetCoreFramework快速开发框架之后端设计

输入校验

  • 在控制器上加上[ApiController]特性,让接口自动校验模型,不必再调用ModelState.IsValid
  • 一般的校验,用自带的校验Attribute即可,如Required,StringLength,Range,RegularExpression等
  • 示例如下
    /// <summary>
    /// dto输入校验
    /// </summary>
    /// <remarks>
    /// 可参考:https://docs.microsoft.com/zh-cn/aspnet/core/mvc/models/validation?view=aspnetcore-3.1
    /// </remarks>
    public class DtoValidateSample : IDto, IValidatableObject
    {
        [Required(ErrorMessage ="必填")]
        public string Reqired { get; set; }
        [StringLength(8, ErrorMessage = "长度不能超过8")]
        public string StringLen { get; set; }
        public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
        {
            var errors = new List<ValidationResult>();
            #region 这里写dto的校验逻辑,如果有问题则加入到errors里
            //errors.Add(new ValidationResult("这是错误内容",new List<string> { "这是核验不通过的字段" }));
            #endregion
            return errors;
        }
    }

统一输出格式

  • 格式约定如下
{
	"Code":2000,//这是接口的状态,只有2000才为成功,其它为失败,失败的原因为msg
	"Data":xxx,//这是真实返回的数据
	"Msg":""//错误信息
}
  • 用ResultFilter实现,在所有的接口返回结果时,统一对结果进行标准格式封闭,GlobalResultFilterAttribute
   /// <summary>
    /// 只有没有进入到ExceptFilter里的才会进入此过滤器
    /// </summary>
    public class GlobalResultFilterAttribute : ResultFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext context)
        {
            if (context.Result is ObjectResult objectResult)//正常返回
            {
                context.Result = new ObjectResult(ApiResultDto.SuccessResult(objectResult.Value));
            }
            else if (context.Result is EmptyResult emptyResult)
            {
                context.Result = new ObjectResult(ApiResultDto.SuccessResult(null));
            }
        }
    }

  • 将GlobalResultFilterAttribute加入到全局过滤器
  • action只需正常返回Data里的类型值即可

异常处理

  • 通常我们项目中的异常会分几种,
    1、业务上的自己定义的异常信息(本文暂称它为业务异常),这些异常信息通常都是要给用户看的。
    2、未捕获的程序异常(本文暂称它为程序异常),这类异常要在开发环境让开发者看到,但在生产环境时,会隐藏异常的详细信息
  • 通常会在如下几个地方对上面的异常进行处理
    1、在MVC管道里
    2、在asp.net core管道里
    下面是两个管道里处理这两种异常的代码

MVC管道处理异常

在接口或接口调用的方法里如果手动throw BusinessException(BusinessException为业务异常的类),会被MVC管理捕获

 public class GlobalExceptionFilterAttribute : ExceptionFilterAttribute
    {
        private ILogger _logger;
        public GlobalExceptionFilterAttribute(ILogger<GlobalExceptionFilterAttribute> logger)
        {
            _logger = logger;
        }
        public override void OnException(ExceptionContext context)
        {

            if (context.Exception is BusinessException businessException)
            {
                // 业务类的异常,返回4000状态,并返回异常内容。模型校验也会返回4000状态和内容
                context.Result = new ObjectResult(ApiResultDto.BadRequestResult(businessException.Message));
                if (_logger != null)
                {
                    _logger.LogWarning(businessException, "businessException");
                }
            }
            else
            {
                context.Result = new ObjectResult(ApiResultDto.ServerErrorResult("未知异常"));
                if (_logger != null)
                {
                    _logger.LogError("exception:{exception} \n innerException:{innerException}", context.Exception?.ToString(), context.Exception?.InnerException?.ToString());
                }
            }

        }
    }

asp.net core管道处理异常

asp.net core 的管理配置是在Startup.cs的Configure方法里,管道异常处理代码如下

//开发环境用异常处理程序页,让开发者能看到异常信息详细
 if (env.IsDevelopment())
{
    app.UseMiniProfiler();
    app.UseDeveloperExceptionPage(); //开发环境用异常处理程序页,让开发者能看到异常信息详细
}
else
{
    // 生产环境异常处理,隐藏异常详细信息,并记录日志
    app.UseExceptionHandler(errorApp =>
    {

        errorApp.Run(async context =>
        {
            var loggerFactory = (ILoggerFactory)context.RequestServices.GetService(typeof(ILoggerFactory));
            var logger = loggerFactory.CreateLogger("UnKnowException");
            var exceptionHandlerPathFeature =
                context.Features.Get<IExceptionHandlerPathFeature>();
            //业务异常
            ApiResultDto responseResultModel;
            if (exceptionHandlerPathFeature?.Error is BusinessException businessException)
            {
                responseResultModel = ApiResultDto.BadRequestResult(businessException.Message);
                if (logger != null)
                {
                    logger.LogError(exceptionHandlerPathFeature?.Error?.ToString());
                }
            }
            else
            {
                responseResultModel = ApiResultDto.BadRequestResult($"程序出错,出于安全考虑,出错信息未能返回,请联系IT进行处理,错误时间{DateTime.Now}");
                if (logger != null)
                {
                    logger.LogError(exceptionHandlerPathFeature?.Error?.ToString());
                }
            }
            context.Response.ContentType = "application/json";
            context.Response.StatusCode = (int)HttpStatusCode.OK;
            await context.Response.WriteAsync(JsonConvert.SerializeObject(responseResultModel));
        });
    });


    //HTTP严格传输安全 让网站可以通知浏览器它不应该再使用HTTP加载该网站,而是自动转换该网站的所有的HTTP链接至更安全的HTTPS。它包含在HTTP的协议头 Strict-Transport-Security 中,在服务器返回资源时带上,换句话说,它告诉浏览器将URL协议从HTTP更改为HTTPS(会更安全),并要求浏览器对每个请求执行此操作。
    //正式环境官方建议用UseHsts和UseHttpsRedirection,
    // 如果反方代理服务器,如ngix已经有配置过http重定向https或是设置hsts,则不需要设置这两句
    //参考: https://docs.microsoft.com/en-us/aspnet/core/security/enforcing-ssl?view=aspnetcore-3.1&tabs=visual-studio
    app.UseHsts();
    app.UseHttpsRedirection();//将所有的http重定向https
}

posted @ 2020-08-07 16:43  shengyu_kmust  阅读(374)  评论(1编辑  收藏  举报