【.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 文档,并封装视图模型验证信息的返回值。通过这些配置,能帮助开发者快速构建和维护高效的应用程序。

相关推荐
九鼎科技-Leo1 小时前
了解 .NET 运行时与 .NET 框架:基础概念与相互关系
windows·c#·.net
Dann Hiroaki2 小时前
GPU架构概述
架构
茶馆大橘2 小时前
微服务系列五:避免雪崩问题的限流、隔离、熔断措施
java·jmeter·spring cloud·微服务·云原生·架构·sentinel
coding侠客3 小时前
揭秘!微服务架构下,Apollo 配置中心凭啥扮演关键角色?
微服务·云原生·架构
lipviolet3 小时前
架构系列---高并发
架构
Phodal4 小时前
架构赋能 AI:知识工程推动下的软件架构数字化
人工智能·架构
lexusv8ls600h4 小时前
微服务设计模式 - 网关路由模式(Gateway Routing Pattern)
spring boot·微服务·设计模式
九鼎科技-Leo4 小时前
什么是 ASP.NET Core?与 ASP.NET MVC 有什么区别?
windows·后端·c#·asp.net·mvc·.net
.net开发4 小时前
WPF怎么通过RestSharp向后端发请求
前端·c#·.net·wpf
曹申阳6 小时前
2. JVM的架构模型和生命周期
jvm·架构