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

本文涉及的产品
云原生内存数据库 Tair,内存型 2GB
云数据库 Redis 版,标准版 2GB
推荐场景:
搭建游戏排行榜
日志服务 SLS,月写入数据量 50GB 1个月
简介: 基于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项目,如下所示。

我们在生成的项目中,看到有一个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信息,如下所示得到很详细的接口注释。

然后调整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类库,如下所示。

然后在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:

 

未授权结果401:

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

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

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

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

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系统错误)等,默认是没有进行处理的

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

其中在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代码中添加使用代码即可,注意添加位置。

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

通过连接字符串(读取配置信息)初始化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中了。

 

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上开发应用 (本随笔)

 

基于SqlSugar的数据库访问处理的封装,支持.net FrameWork和.net core的项目调用  

 

基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中(2)

 

基于SqlSugar的数据库访问处理的封装,支持多数据库并使之适应于实际业务开发中

专注于代码生成工具、.Net/.NetCore 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架等框架产品。
 转载请注明出处:撰写人:伍华聪  http://www.iqidi.com

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
15天前
|
开发框架 JavaScript 前端开发
如何选择合适的Web开发框架?
【9月更文挑战第1天】如何选择合适的Web开发框架?
37 1
|
9天前
|
Web App开发 前端开发 JavaScript
Web前端项目的跨平台桌面客户端打包方案之——CEF框架
Chromium Embedded Framework (CEF) 是一个基于 Google Chromium 项目的开源 Web 浏览器控件,旨在为第三方应用提供嵌入式浏览器支持。CEF 隔离了底层 Chromium 和 Blink 的复杂性,提供了稳定的产品级 API。它支持 Windows、Linux 和 Mac 平台,不仅限于 C/C++ 接口,还支持多种语言。CEF 功能强大,性能优异,广泛应用于桌面端开发,如 QQ、微信、网易云音乐等。CEF 开源且采用 BSD 授权,商业友好,装机量已超 1 亿。此外,GitHub 项目 CefDetector 可帮助检测电脑中使用 CEF
49 3
|
10天前
|
关系型数据库 Java MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【9月更文挑战第6天】在Linux环境下安装JDK 1.8、Tomcat和MariaDB是搭建Java Web应用的关键步骤。本文详细介绍了使用apt-get安装OpenJDK 1.8、下载并配置Tomcat,以及安装和安全设置MariaDB(MySQL的开源分支)的方法。通过这些步骤,您可以快速构建一个稳定、高效的开发和部署环境,并验证各组件是否正确安装和运行。这为您的Java Web应用提供了一个坚实的基础。
26 0
|
16天前
|
数据库 开发者 Java
颠覆传统开发:Hibernate与Spring Boot的集成,让你的开发效率飞跃式提升!
【8月更文挑战第31天】在 Java 开发中,Spring Boot 和 Hibernate 已成为许多开发者的首选技术栈。Spring Boot 简化了配置和部署过程,而 Hibernate 则是一个强大的 ORM 框架,用于管理数据库交互。将两者结合使用,可以极大提升开发效率并构建高性能的现代 Java 应用。本文将通过代码示例展示如何在 Spring Boot 项目中集成 Hibernate,并实现基本的数据库操作,包括添加依赖、配置数据源、创建实体类和仓库接口,以及在服务层和控制器中处理 HTTP 请求。这种组合不仅简化了配置,还提供了一套强大的工具来快速开发现代 Java 应用程序。
29 0
|
16天前
|
数据库 开发者 Java
Hibernate映射注解的魔力:实体类配置的革命,让你的代码量瞬间蒸发!
【8月更文挑战第31天】Hibernate 是一款出色的对象关系映射框架,简化了 Java 应用与数据库的交互。其映射注解让实体类配置变得直观简洁。本文深入剖析核心概念与使用技巧,通过示例展示如何简化配置。
23 0
|
16天前
|
数据库 开发者 Java
数据战争:Hibernate的乐观与悲观锁之争,谁将主宰并发控制的王座?
【8月更文挑战第31天】在软件开发中,数据一致性至关重要,尤其是在多用户并发访问环境下。Hibernate 作为 Java 社区常用的 ORM 框架,提供了乐观锁和悲观锁机制来处理并发问题。乐观锁假设数据不易冲突,通过版本号字段 (`@Version`) 实现;悲观锁则假定数据易冲突,在读取时即加锁。选择哪种锁取决于具体场景:乐观锁适合读多写少的情况,减少锁开销;悲观锁适合写操作频繁的场景,避免数据冲突。正确应用这些机制可提升应用程序的健壮性和效率。
26 0
|
16天前
|
Java Maven Android开发
解锁Web开发新技能:从零开始的Struts 2之旅——让你的Java编程之路更加宽广,首个应用实例带你飞!
【8月更文挑战第31天】对于初学者,掌握 Struts 2 框架不仅能提升 Web 开发能力,还能深入了解 MVC 架构。Struts 2 是一个基于 Servlet 的 Java 框架,提供表单验证、文件上传、国际化等功能,便于快速构建易维护的 Web 应用。本文通过示例演示如何从零开始搭建环境并创建一个简单的 Struts 2 项目,包括配置 `struts.xml`、编写 Action 类及视图文件,并配置 web.xml。通过这些步骤,你将学会基本的开发流程,为进一步学习高级功能打下基础。
27 0
|
16天前
|
Java 开发者 数据安全/隐私保护
Struts 2 拦截器机制震撼登场!紧跟技术热点,解锁高效开发密码,引发开发者情感共鸣
【8月更文挑战第31天】在 Java Web 开发中,Struts 2 框架凭借其强大功能和灵活性深受开发者喜爱。其中,拦截器机制是 Struts 2 的重要特性之一,它允许我们在请求处理过程中执行自定义操作,如日志记录、权限验证和数据转换等。拦截器通过实现 `com.opensymphony.xwork2.interceptor.Interceptor` 接口并在 `struts.xml` 中配置即可使用。合理利用拦截器可实现代码解耦与复用,提升开发效率和系统可维护性。框架还提供了如 `params` 和 `servletConfig` 等内置拦截器供开发者按需选用。
24 0
|
16天前
|
开发者 Java UED
大文件传输不再头疼:揭秘Struts 2如何轻松应对文件上传与下载难题!
【8月更文挑战第31天】在Web应用开发中,文件上传与下载至关重要。Struts 2作为主流Java EE框架,凭借Commons FileUpload及文件上传拦截器简化了相关操作。本文探讨Struts 2在文件传输上的优势,通过具体配置与代码示例,展示如何设置最大文件大小、使用`fileUpload`拦截器以及实现文件上传与下载功能。对于大文件传输,Struts 2不仅能够轻松应对,还支持上传进度显示,有效提升了用户体验。总体而言,Struts 2为文件传输提供了高效便捷的解决方案,助力开发者构建稳定可靠的Web应用。然而,在处理大文件时需兼顾网络带宽与服务器性能,确保传输顺畅。
36 0
|
16天前
|
开发者 Java 前端开发
Struts 2验证框架:如何让数据校验成为Web开发的隐形守护者?揭秘前后端一致性的秘诀
【8月更文挑战第31天】在现代Web开发中,数据验证对确保应用健壮性和良好用户体验至关重要。随着前后端分离架构的普及,保证数据校验一致性尤为关键。Struts 2 验证框架基于 JavaBean 验证 API(JSR 303/JSR 380),允许开发者通过注解或 XML 配置轻松定义验证规则,确保输入数据在执行业务逻辑前已通过验证。此外,Struts 2 支持与前端 JavaScript 验证相结合,确保前后端数据校验一致,提升用户体验。通过注解、XML 配置和资源文件,开发者可以轻松定义和调整验证规则,实现前后端一致的数据校验,提高应用健壮性。
25 0

热门文章

最新文章