框架是一个项目所必须的,是一组预先编写的代码库和工具,提供了一个可以复用的结构,以帮助开发者快速构建应用程。即使项目目前是一个单体应用,我们还是需要先设计框架的,但是我们不可能在项目的初期就编写出一个完美的框架,因此在项目开发中我们有极大的可能对框架改动(新增一些封装、删除一些代码,修改一些代码等)。在这一节我们先简单的来设计一个小框架。
一、安装Nuget包
项目中我们需要对数据库进行操作,视图模型数据转换为数据库模型数据,以及操作Token,因此我们需要安装和它们相关的 nuget 包。
1.1 Pomelo.EntityFrameworkCore.MySql
Pomelo.EntityFrameworkCore.MySql
是一个开源的 EF Core 提供程序,通过它我们可以操作 MySQL 数据库,同时我们还需要安装 Microsoft.EntityFrameworkCore
和 Microsoft.EntityFrameworkCore.Tools
。Pomelo.EntityFrameworkCore.MySql
依赖于 Microsoft.EntityFrameworkCore
,Microsoft.EntityFrameworkCore.Tools
是一个工具包,它包含一组命令行工具,帮助我们在 .NET 项目中使用 EF Core 进行常见任务,例如数据库迁移、模型生成和数据库更新等。
要安装它们,只需要包管理器中搜索他们的名字,选择最新版本(对应.NET 8的最新版本)安装即可,当然你也可以在程序包管理控制台中输入命令来安装它们。
Install-Package Pomelo.EntityFrameworkCore.MySql
Install-Package Microsoft.EntityFrameworkCore
Install-Package Microsoft.EntityFrameworkCore.Tools
1.2 AutoMapper
AutoMapper
是一个对象与对象映射工具,用于在 .NET 应用程序中简化对象之间的数据传输,主要功能是将一个对象的属性映射到另一个对象上,我们把它用在视图模型数据转换为数据库模型数据中。在包管理器中搜索AutoMapper安装即可,也可以在程序包管理控制台中输入命令来安装。
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安装即可,也可以在程序包管理控制台中输入命令来安装。
Install-Package Microsoft.AspNetCore.Authentication.JwtBearer
二、配置Jwt
打开 appsettings.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 文件中加入如下代码:
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
方法用于添加和配置身份验证服务,其中 DefaultAuthenticateScheme
和 DefaultChallengeScheme
指定了 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
,类代码如下:
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 *** 文件中输入如下内容:
"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 文件中输入如下内容:
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文档的文件即可。
六、添加通用类
最后,我们要添加四个通用类:PageRequestViewModel、PageResponseViewModel、BaseModel、ResponseData。它们的作用是:
- PageRequestViewModel:分页查询请求基类
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;
}
- PageResponseViewModel:分页查询响应基类
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>();
}
- BaseModel:数据库映射类基类
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;
}
- ResponseData:返回给客户端的响应封装
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 文件,输入如下代码:
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 文档,并封装视图模型验证信息的返回值。通过这些配置,能帮助开发者快速构建和维护高效的应用程序。