【.NET 8 实战--孢子记账--从单体到微服务】--编写服务端框架

框架是一个项目所必须的,是一组预先编写的代码库和工具,提供了一个可以复用的结构,以帮助开发者快速构建应用程。即使项目目前是一个单体应用,我们还是需要先设计框架的,但是我们不可能在项目的初期就编写出一个完美的框架,因此在项目开发中我们有极大的可能对框架改动(新增一些封装、删除一些代码,修改一些代码等)。在这一节我们先简单的来设计一个小框架。

一、安装Nuget包

项目中我们需要对数据库进行操作,视图模型数据转换为数据库模型数据,以及操作Token,因此我们需要安装和它们相关的 nuget 包。

1.1 Pomelo.EntityFrameworkCore.MySql

Pomelo.EntityFrameworkCore.MySql 是一个开源的 EF Core 提供程序,通过它我们可以操作 MySQL 数据库,同时我们还需要安装 Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.ToolsPomelo.EntityFrameworkCore.MySql 依赖于 Microsoft.EntityFrameworkCoreMicrosoft.EntityFrameworkCore.Tools 是一个工具包,它包含一组命令行工具,帮助我们在 .NET 项目中使用 EF Core 进行常见任务,例如数据库迁移、模型生成和数据库更新等。

要安装它们,只需要包管理器中搜索他们的名字,选择最新版本(对应.NET 8的最新版本)安装即可,当然你也可以在程序包管理控制台中输入命令来安装它们。

shell 复制代码
Install-Package Pomelo.EntityFrameworkCore.MySql
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Tools
1.2 AutoMapper

AutoMapper 是一个对象与对象映射工具,用于在 .NET 应用程序中简化对象之间的数据传输,主要功能是将一个对象的属性映射到另一个对象上,我们把它用在视图模型数据转换为数据库模型数据中。在包管理器中搜索AutoMapper安装即可,也可以在程序包管理控制台中输入命令来安装。

shell 复制代码
Install-Package AutoMapper
1.3 Microsoft.AspNetCore.Authentication.JwtBearer

Microsoft.AspNetCore.Authentication.JwtBearer 是一个ASP.NET Core 中间件组件,它为 JWT 身份验证提供支持。JwtBearer 中间件允许 ASP.NET Core 应用程序验证传入的 JWT 并授权访问保护的资源。在包管理器中搜索Microsoft.AspNetCore.Authentication.JwtBearer安装即可,也可以在程序包管理控制台中输入命令来安装。

shell 复制代码
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer

二、配置Jwt

打开 appsettings.json 文件,在文件中添加如下内容:

json 复制代码
  "JWT": {
    "ValidIssuer": "spore.miaoshu.xyz",
    "ValidAudience": "sporeapi.miaoshu.xyz",
    "IssuerSigningKey": "9$1_zC$<2b4dVS|dQ%c&bD E{Migyy3h{z#@"
  }

ValidIssuer 是在 JWT 身份验证过程中用于验证令牌来源的一项配置,它指定了期望的令牌签发者。在身份验证过程中,JWT 令牌中的 iss 声明需要匹配 ValidIssuer 的值,只有这样令牌才会被认为是有效的。ValidAudience 同样是在 JWT 身份验证过程中用于验证令牌目标的一项配置,它指定了期望的受(令牌的目标用户或服务),JWT 令牌中的 aud 声明需要匹配 ValidAudience 的值。IssuerSigningKey 是在 JWT 身份验证过程中用于验证令牌签名的密钥,用于指定用于签名 JWT 令牌的密钥,确保令牌的完整性和真实性。

appsettings.json 文件配置完后,我们在 Program.cs 文件中加入如下代码:

csharp 复制代码
            ConfigurationManager configurationManager= builder.Configuration;
            // 配置 JWT 验证
            builder.Services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
            }).AddJwtBearer(options =>
            {
                options.TokenValidationParameters = new TokenValidationParameters
                {
                    ValidateIssuer = true,
                    ValidateAudience = true,
                    ValidateLifetime = true,
                    ValidateIssuerSigningKey = true,
                    ValidIssuer = configurationManager["JWT:ValidIssuer"],
                    ValidAudience = configurationManager["JWT:ValidAudience"],
                    IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configurationManager["JWT:IssuerSigningKey"])),
                    ClockSkew = TimeSpan.Zero 
                };
            });

在上面的代码中,有两个很关键的地方,一个是 AddAuthentication 方法,一个是 AddJwtBearer 方法。
AddAuthentication 方法用于添加和配置身份验证服务,其中 DefaultAuthenticateSchemeDefaultChallengeScheme 指定了 JWT 身份验证的默认身份验证方案为JwtBearerDefaults.AuthenticationScheme
AddJwtBearer 方法用于配置 JWT Bearer 身份验证,TokenValidationParameters 类用于设置令牌验证参数,确保令牌的有效性和安全性。在这个代码段中我们告诉 JWT Bearer 需要验证令牌的签发者(ValidateIssuer),并指定了令牌的签发者(ValidIssuer)。同时也告知 JWT Bearer 需要验证令牌的受众(ValidateAudience),以及指定了令牌的受众(ValidAudience)。通过 ValidateLifetime 设置需要验证令牌的有效期,也通过 ValidateIssuerSigningKey 设置了示需要验证令牌的签名,这时JWT 的签名将使用 IssuerSigningKey 进行验证,确保令牌没有被篡改。ClockSkew 设置为 TimeSpan.Zero,表示不允许任何时间偏移。

Tip:Jwt 在这里就不详细讲解了,需要来学习的可以关注我写的关于Jwt的文章。

三、配置AutoMapper

Program.cs 中加入代码 builder.Services.AddAutoMapper(Assembly.GetExecutingAssembly()); 就完成了 AutoMapper 的配置。AutoMapper 会在程序启动时去扫描当前程序集中所有继承了 Profile 的类,然后加载他们。

Tip:AutoMapper 在这里就不详细讲解了,需要来学习的可以关注我写的关于AutoMapper的文章。

四、配置数据库

首先我们需要创建数据库上下文类SporeAccountingDBContext,类代码如下:

csharp 复制代码
using Microsoft.EntityFrameworkCore;
using SporeAccounting.Models;

namespace SporeAccounting;

/// <summary>
/// 数据库连接上下文
/// </summary>
public class SporeAccountingDBContext : DbContext
{
    IConfiguration _dbConfig;
    public SporeAccountingDBContext(IConfiguration dbConfig)
    {
        _dbConfig = dbConfig;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var serverVersion = ServerVersion.AutoDetect(_dbConfig.GetConnectionString("MySQLConnection"));
        optionsBuilder.UseMySql(_dbConfig.GetConnectionString("MySQLConnection"), serverVersion);
        optionsBuilder.UseLoggerFactory(LoggerFactory.Create(builder =>
        {
            //控制台打印SQL语句
            builder.AddConsole();
        }));

    }
}

在上面代码中,SporeAccountingDBContext 类继承了 DbContext,并且重写了 OnConfiguring 方法,在这个方法中我们读取了 appsettings.json 文件中的数据库连接字符串,同时配置了在控制台打印 EF Core 生成的代码。

接下来,我们配置数据库连接字符串,在 ***appsettings.json *** 文件中输入如下内容:

json 复制代码
  "ConnectionStrings": {
    "MySQLConnection": "server=47.95.36.237;port=3306;database=SporeAccounting;user=root;pwd=123asdasd;"
  }

到此,数据库的配置就完成了。

Tip:EF Core 在这里就不详细讲解了,需要来学习的可以关注我写的关于EF Core 的文章。

五、配置 Swagger

Swagger 是一个用于生成和展示 API 文档的工具,可以帮助你为 API 生成交互式文档,方便我们测试和调试。在 Program.cs 文件中输入如下内容:

csharp 复制代码
builder.Services.AddSwaggerGen(s =>
{
    //添加安全定义
    s.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "请输入token,格式为 Bearer XXXXX(注意中间必须有空格)",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey,
        BearerFormat = "JWT",
        Scheme = "Bearer"
    });
    //添加安全要求
    s.AddSecurityRequirement(new OpenApiSecurityRequirement {
        {
            new OpenApiSecurityScheme{
                Reference =new OpenApiReference{
                    Type = ReferenceType.SecurityScheme,
                    Id ="Bearer"
                }
            },new string[]{ }
        }
    });
});

在这段代码中,我们为 Swagger 开启了 Token 验证功能,并设置了 BearerFormat 为JWT。然后我们邮件项目属性,打开属性设置页面,找到输出 选项卡,勾选生成包含API文档的文件 即可。

六、添加通用类

最后,我们要添加四个通用类:PageRequestViewModelPageResponseViewModelBaseModelResponseData。它们的作用是:

  1. PageRequestViewModel:分页查询请求基类
csharp 复制代码
using System.ComponentModel.DataAnnotations;

namespace SporeAccounting.BaseModels.ViewModel.Request;

/// <summary>
/// 分页查询请求基类
/// </summary>
public class PageRequestViewModel
{
    /// <summary>
    /// 请求的页码
    /// </summary>
    [Range(1, int.MaxValue,ErrorMessage = $"{nameof(PageNumber)}不能小于1大于2147483647")]
    [Required(ErrorMessage = $"{nameof(PageNumber)}不能为空")]
    public int PageNumber { get; set; } = 1;

    /// <summary>
    /// 每页大小
    /// </summary>
    [Range(1, 50, ErrorMessage = $"{nameof(PageSize)}不能小于1大于50")]
    [Required(ErrorMessage = $"{nameof(PageSize)}不能为空")]
    public int PageSize { get; set; } = 20;

}
  1. PageResponseViewModel:分页查询响应基类
csharp 复制代码
namespace SporeAccounting.BaseModels.ViewModel.Response;

/// <summary>
/// 分页查询响应基类
/// </summary>
public class PageResponseViewModel<T>
{
    /// <summary>
    /// 总页数
    /// </summary>
    public int PageCount { get; set; } = 0;
    /// <summary>
    /// 总行数
    /// </summary>
    public int RowCount { get; set; }= 0;
    /// <summary>
    /// 返回的数据集合
    /// </summary>
    public List<T> Data { get; set; }=new List<T>();
}
  1. BaseModel:数据库映射类基类
csharp 复制代码
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;

namespace SporeAccounting.BaseModels;

/// <summary>
/// 数据库映射类基类
/// </summary>
public class BaseModel
{
    /// <summary>
    /// 表数据唯一值
    /// </summary>
    [Key]
    [Column(TypeName = "nvarchar(36)")]
    [Required]
    public string Id { get; set; }
    /// <summary>
    /// 创建时间
    /// </summary>
    [Required]
    [Column(TypeName = "datetime")]
    public DateTime CreateDateTime { get; set; }= DateTime.Now;
    /// <summary>
    /// 创建用户
    /// </summary>
    [Required]
    [Column(TypeName = "nvarchar(36)")]
    public string CreateUserId { get; set; }
    /// <summary>
    /// 修改时间
    /// </summary>
    [Required]
    [Column(TypeName = "datetime")]
    public DateTime UpdateDateTime { get; set; } = DateTime.Now;
    /// <summary>
    /// 修改用户
    /// </summary>
    [Required]
    [Column(TypeName = "nvarchar(36)")]
    public string UpdateUserId { get; set; }
    /// <summary>
    /// 删除时间
    /// </summary>
    [Column(TypeName = "datetime")]
    public DateTime DeleteDateTime { get; set; } = DateTime.Now;
    /// <summary>
    /// 删除用户
    /// </summary>
    [Column(TypeName = "nvarchar(36)")]
    public string DeleteUserId { get; set; }
    /// <summary>
    /// 是否删除(物理删除)
    /// </summary>
    [Required]
    [Column(TypeName = "bool")]
    public bool IsDeleted { get; set; }=false;
}
  1. ResponseData:返回给客户端的响应封装
csharp 复制代码
using System.Net;

namespace SporeAccounting.BaseModels;

/// <summary>
/// 返回给客户端的响应封装
/// </summary>
public class ResponseData<T>
{
    /// <summary>
    /// 返回给客户端的响应封装
    /// </summary>
    /// <param name="statusCode">http 状态码</param>
    /// <param name="errorMessage">错误信息</param>
    /// <param name="data">返回数据</param>
    public ResponseData(HttpStatusCode statusCode, string errorMessage, T data)
    {
        StatusCode = statusCode;
        ErrorMessage = errorMessage;
        Data = data;
    }

    /// <summary>
    /// 响应的Code
    /// </summary>
    public HttpStatusCode StatusCode { get; set; } = HttpStatusCode.OK;
    /// <summary>
    /// 错误信息
    /// </summary>
    public string ErrorMessage { get; set; }
    /// <summary>
    /// 数据
    /// </summary>
    public T Data { get; set; }
}

七、封装视图模型验证信息返回值

打开 Program.cs 文件,输入如下代码:

csharp 复制代码
builder.Services.Configure<ApiBehaviorOptions>(options =>
{
    options.InvalidModelStateResponseFactory = actionContext =>
    {
        //获取验证失败的模型字段 
        var errors = actionContext.ModelState
            .Where(s => s.Value != null && s.Value.ValidationState == ModelValidationState.Invalid)
            .SelectMany(s => s.Value!.Errors.ToList())
            .Select(e => e.ErrorMessage)
            .ToList();

        // 统一返回格式
        var result = new ResponseData<string>(HttpStatusCode.BadRequest, string.Join("\r\n", errors.ToArray()), "");
        return new BadRequestObjectResult(result);
    };
});

在上面代码中,我们获取了验证失败的模型的字段的错误信息,并通过刚才我们定义的 ResponseData 类重新封装了一下,返回给了客户端。

七、总结

本文详细讲解了如何配置 ASP.NET Core 应用中的几个关键组件。首先,介绍了安装和配置 NuGet 包,包括数据库操作、对象映射、JWT 身份验证。接着,展示了如何在 appsettings.json 中配置 JWT 验证,并在 Program.cs 中设置相关服务。随后,讲解了如何配置 AutoMapper、数据库上下文、以及数据库连接。最后,介绍了如何配置 Swagger 生成 API 文档,并封装视图模型验证信息的返回值。通过这些配置,能帮助开发者快速构建和维护高效的应用程序。

相关推荐
Wgllss38 分钟前
6种Kotlin中单例模式写法,特点及应用场景指南
android·架构·android jetpack
fs哆哆2 小时前
在VB.net中,文本插入的几个自定义函数
服务器·前端·javascript·html·.net
专注VB编程开发20年2 小时前
c#,vb.net LockObject ,多线程锁,多线程安全字典ConcurrentDictionary
开发语言·c#·.net
Linux技术支持工程师3 小时前
docker镜像封装与发布微服务学习
学习·docker·微服务
ponnylv3 小时前
微服务拆分之术与道:从原则到实践的深度解析
微服务·云原生·架构
Hellyc3 小时前
Feign进行微服务转发的实现
java·数据库·微服务
brzhang3 小时前
我靠!B站上那些炫酷的数学动画,原来是这个Python库搞定的?!
前端·后端·架构
星辰大海的精灵3 小时前
SpringBoot 热部署更新术让代码丝滑上线
spring boot·后端·架构
hello早上好4 小时前
Spring作用域与生命周期
java·后端·架构
腾讯云开发者4 小时前
为什么我们需要工程师文化?
架构