基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

我前面几篇随笔介绍了关于几篇关于SqlSugar的基础封装,已经可以直接应用在Winform项目开发上,并且基础接口也通过了单元测试,同时测试通过了一些Winform功能页面;本篇随笔继续深化应用开发,着手在在.net6框架的Web API上开发应用,也就是基于.net core的Web API应用开发,这样可以应用在不同的前端接入上。本篇随笔主要介绍基于.net6框架的Web API的相关整合开发内容,内容涉及到Swagger的整合支持、SeriLog的支持、JWT鉴权和用户身份信息缓存、基类控制器封装、自动注入接口对象、统一结果封装、统一异常处理等方面。

 1、创建.netcore WebApi项目并添加相关支持

本篇随笔主要从基础框架开发创建,因此使用VS2022添加一个基于.net core6的WebAPI项目,如下所示。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

我们在生成的项目中,看到有一个Program.cs的代码文件,里面代码比较简洁,我们逐步调整并添加自己的相关代码即可。

在其中可以看到

builder.Services.AddSwaggerGen();

这个是简单的Swagger注释支持,我们如果需要定义更多的信息,可以采用下面的代码。

#region 添加swagger注释  //builder.Services.AddSwaggerGen();  builder.Services.AddSwaggerGen(c => {     c.SwaggerDoc("v1", new OpenApiInfo     {         Version = "v1",         Title = "Api"     });     c.IncludeXmlComments(Path.Combine(basePath, "SugarWebApi.xml"), true);//WebAPI项目XML文件     c.IncludeXmlComments(Path.Combine(basePath, "SugarProjectCore.xml"), true);//其他项目所需的XML文件      c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme     {         Description = "Value: Bearer {token}",         Name = "Authorization",         In = ParameterLocation.Header,         Type = SecuritySchemeType.ApiKey,         Scheme = "Bearer"     });     c.AddSecurityRequirement(new OpenApiSecurityRequirement()     {       {         new OpenApiSecurityScheme         {           Reference = new OpenApiReference           {             Type = ReferenceType.SecurityScheme,             Id = "Bearer"           },Scheme = "oauth2",Name = "Bearer",In = ParameterLocation.Header,         },new List<string>()       }     }); }); #endregion

上面的代码除了添加对应控制器的接口信息外,还增加了一个相关服务类的接口定义,便于我们查看详细的xml信息,如下所示得到很详细的接口注释。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 然后调整Swagger UI支持的代码如下所示。

// Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) {     app.UseSwagger();     app.UseSwaggerUI(); }

 

另外,我们的Web API控制器,需要集成JWT Bear 认证的处理的,添加认证代码如下所示。

//JWT Bear 认证 builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options => {     options.TokenValidationParameters = new TokenValidationParameters     {         //非固定可选可加         ValidateIssuer = true,         ValidIssuer = builder.Configuration["Jwt:Issuer"],         ValidateAudience = true,         ValidAudience = builder.Configuration["Jwt:Audience"],          ValidateLifetime = true,//时间         ClockSkew = TimeSpan.Zero,         IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Secret"]))     }; });

这里面的代码读取配置信息的,我们可以在appSettings.json中配置JWT的一些键值。

  "Jwt": {     "Secret": "your-256-bit-secret",     "Issuer": "iqidi.com",     "Audience": "api"   }

另外,为了在.net core中输出日志,可以使用SeriLog组件进行处理。

添加相关的nuget类库,如下所示。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 然后在Program.cs中添加初始化日志代码即可。

//初始化日志 Log.Logger = new LoggerConfiguration()        .MinimumLevel.Debug()    //最小记录级别        //对其他日志进行重写,除此之外,目前框架只有微软自带的日志组件        .MinimumLevel.Override(source: "Microsoft", minimumLevel: Serilog.Events.LogEventLevel.Error)         .Enrich.FromLogContext() //记录相关上下文信息         .WriteTo.Console()   //输出到控制台        .WriteTo.File("logs/log.txt", rollingInterval: RollingInterval.Day)  //输出到本地文件         .CreateLogger();

 

2、统一结果封装和异常处理

在Web API的控制器返回信息中,我们为了方便使用JSON信息,往往需要对返回结果进行封装,让它们返回指定格式的数据,如下所示。

正常结果200:

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 

 未授权结果401:

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 关于接口数据格式的统一封装,我们定义一个WrapResultFilter,以及需要一个不封装的属性标识DontWrapResultAttribute,默认是统一封装返回的结果。

    /// <summary>     /// 禁用封装结果     /// </summary>     [AttributeUsage(AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Method, AllowMultiple = false, Inherited = false)]     public class DontWrapResultAttribute : Attribute     {     }

而统一封装的处理,需要继承ActionFilterAttribute并重写OnResultExecuting处理操作。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 里面的主要逻辑就是对结果内容进行统一的再次封装,如下所示主要的逻辑代码。

if (context.Result is ObjectResult objRst) {     if (objRst.Value is AjaxResponse)         return;      context.Result = new ObjectResult(new AjaxResponse     {         Success = true,         Error = null,         TargetUrl = string.Empty,         UnAuthorizedRequest = false,         Result = objRst.Value     }); }

除了常规的正常返回内容进行封装,也需要对异常进行拦截,并对结果进行封装,因此需要继承ExceptionFilterAttribute并添加一个异常处理的过滤器进行处理,并重写OnException的操作即可。

    /// <summary>     /// 自定义异常处理     /// </summary>     public class GlobalExceptionFilter : ExceptionFilterAttribute     {         /// <summary>         /// 异常处理封装         /// </summary>         /// <param name="context"></param>         public override void OnException(ExceptionContext context)         {             if (!context.ExceptionHandled)             {                 //异常返回结果包装                 var content = new AjaxResponse()                 {                     Success = false,                     Error = new ErrorInfo(0, context.Exception.Message, context.Exception.StackTrace),                     TargetUrl = string.Empty,                     UnAuthorizedRequest = false,                     Result = null                 };                  //日志记录                 Log.Error(context.Exception, context.Exception.Message);                  context.ExceptionHandled = true;                 context.Result = new ApplicationErrorResult(content);                 context.HttpContext.Response.StatusCode = (int)HttpStatusCode.InternalServerError;             }         }

因此为了拦截相关的处理,我们在Program.cs中添加以下代码进行拦截。

//控制器添加自定义过滤器 builder.Services.AddControllers(options=> {     options.Filters.Add<WrapResultFilter>(); //统一结果封装处理     options.Filters.Add<GlobalExceptionFilter>();//自定义异常处理 }); //所有控制器启动身份验证 builder.Services.AddMvc(options => {     options.Filters.Add(new AuthorizeFilter());//所有MVC服务默认添加授权标签 });

并调整代码,添加认证和授权验证的代码处理。

app.UseAuthentication();  app.UseAuthorization();

对于一些系统异常的处理(如401未授权、400未找到接口、500系统错误)等,默认是没有进行处理的

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 我们如果要拦截,就另外需要添加一个中间件的方式来处理信息流,如下所示。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 其中在Invoke的函数处理中,统一处理不同的异常即可。

public async Task Invoke(HttpContext context) {     try     {         await next(context);     }     catch (Exception ex)     {         var statusCode = context.Response.StatusCode;         if (ex is ArgumentException)         {             statusCode = 200;         }         await HandleExceptionAsync(context, statusCode, ex.Message);     }     finally     {         var statusCode = context.Response.StatusCode;         var msg = "";         if (statusCode == 401)         {             msg = "未授权 " + context.Response.Headers["WWW-Authenticate"];          }         else if (statusCode == 404)         {             msg = "未找到服务";         }         else if(statusCode == 500)         {             msg = "系统错误";         }         else if (statusCode == 502)         {             msg = "请求错误";         }         else if (statusCode != 200)         {             msg = "未知错误";         }          if (!string.IsNullOrWhiteSpace(msg))         {             await HandleExceptionAsync(context, statusCode, msg);         }     } }

并添加一个扩展类方法,用于快速使用中间件方式调用。

    /// <summary>     /// 自定义错误处理的扩展方法     /// </summary>     public static class ErrorHandlingExtensions     {         public static IApplicationBuilder UseErrorHandling(this IApplicationBuilder builder)         {             return builder.UseMiddleware<ErrorHandlingMiddleware>();         }     }

最后在program.cs代码中添加使用代码即可,注意添加位置。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

另外,为了把用户身份信息缓存起来,我们可以使用Redis进行缓存处理,因此在项目中使用CRedis的封装类库进行操作Redis

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

通过连接字符串(读取配置信息)初始化Redis的代码如下所示。

//初始化Redis RedisHelper.Initialization(new CSRedisClient(builder.Configuration["CSRedis:ConnectString"]));

其中appSettings.json信息如下所示。

{   "ConnectionStrings": {     "Default": "Server=.; Database=WeixinBootstrap2; Trusted_Connection=True;",     "Oracle": "Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=localhost)(PORT=1521))(CONNECT_DATA=(SERVER=DEDICATED)(SERVICE_NAME=orcl)));User ID=C##ABP;Password=abp",     "MySql": "Server=localhost;Database=myprojectdb;Uid=root;Pwd=123456;",     "PostgreSQL": "Server=localhost;Port=5432;Database=myprojectdb;User Id=postgres;Password=123456"   },   "DbSetting": {     "DefaultDb": "Default",     "ComponentDbType": "sqlserver"   },   "CSRedis": {     "ConnectString": "127.0.0.1:6379"   },   "Jwt": {     "Secret": "your-256-bit-secret",     "Issuer": "iqidi.com",     "Audience": "api"   },   "Logging": {     "LogLevel": {       "Default": "Information",       "Microsoft.AspNetCore": "Warning"     }   },   "AllowedHosts": "*", }

如果允许登录授权请求成功的话,那么对应的用户省份缓存也就记录在Redis中了。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用

 

3、接口对象的依赖注入处理

我们在.net core的Web API中调用相关处理,我们往往是使用接口来调用相关的处理的。

启动Web API的时候通过手工或者自动注入接口对象的方式,然后在控制器里面的构造函数中通过依赖注入方式使用接口即可。

如果是手工注入,那么你确定在Web API项目中所有用到的业务访问接口,都需要提取注入。

    services.AddSingleton<IDictDataService, DictDataService>();     services.AddSingleton<IDictTypeService, DictTypeService>();     services.AddSingleton<ICustomerService, CustomerService>();     services.AddScoped<IUserService, UserService>();

但是这样接口实现一旦很多,手工加入肯定繁琐而且低效了,因此需要考虑自动注入所有相关的服务接口为佳。

为了实现这个自动注入的目标,首先我们先定义几个不同生命周期的接口声明。

    //用于定义这三种生命周期的标识接口      public interface IDependency     {     }      /// <summary>     /// 瞬时(每次都重新实例)     /// </summary>     public interface ITransientDependency : IDependency     {     }      /// <summary>     /// 单例(全局唯一)     /// </summary>     public interface ISingletonDependency : IDependency     {              }      /// <summary>     /// 一个请求内唯一(线程内唯一)     /// </summary>     public interface IScopedDependency : IDependency     {     }

然后通过遍历相关的DLL,然后实现所有实现指定接口的类对象,统一加入即可,如下代码所示。

            var baseType = typeof(IDependency);             var path = AppDomain.CurrentDomain.RelativeSearchPath ?? AppDomain.CurrentDomain.BaseDirectory;             var getFiles = Directory.GetFiles(path, "*.dll").Where(Match);  //.Where(o=>o.Match())             var referencedAssemblies = getFiles.Select(Assembly.LoadFrom).ToList();  //.Select(o=> Assembly.LoadFrom(o))                       var ss = referencedAssemblies.SelectMany(o => o.GetTypes());

然后进一步进行对接口的判断,如下所示。

    var types = referencedAssemblies         .SelectMany(a => a.DefinedTypes)         .Select(type => type.AsType())         .Where(x => x != baseType && baseType.IsAssignableFrom(x)).ToList();     var implementTypes = types.Where(x => x.IsClass).ToList();     var interfaceTypes = types.Where(x => x.IsInterface).ToList();     foreach (var implementType in implementTypes)     {         if (typeof(IScopedDependency).IsAssignableFrom(implementType))         {             var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));             if (interfaceType != null)                 services.AddScoped(interfaceType, implementType);         }         else if (typeof(ISingletonDependency).IsAssignableFrom(implementType))         {             var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));             if (interfaceType != null)                 services.AddSingleton(interfaceType, implementType);         }         else         {             var interfaceType = interfaceTypes.FirstOrDefault(x => x.IsAssignableFrom(implementType));             if (interfaceType != null)                 services.AddTransient(interfaceType, implementType);         }     }

然后统一调用即可。

//配置依赖注入访问数据库 ServiceInjection.ConfigureRepository(builder.Services);

这样我们在对应的WebAPI 控制器中就可以方便的使用接口的构造函数注入方式了。

    /// <summary>     /// 客户信息的控制器对象     /// </summary>     public class CustomerController : BusinessController<CustomerInfo, string, CustomerPagedDto>     {         private ICustomerService _customerService;          /// <summary>         /// 构造函数,并注入基础接口对象         /// </summary>         /// <param name="customerService"></param>         public CustomerController(ICustomerService customerService) :base(customerService)         {             this._customerService = customerService;         }     }

以上就是我们在创建.net Core项目的Web API项目中碰到的一些常见问题的总结,希望对大家有所帮助。

 

相关系类文章如下所示。

基于SqlSugar的数据库访问处理的封装,在.net6框架的Web API上开发应用  (本随笔)
 

发表评论

相关文章