本文介绍从零到一搭建基于netcore6.0版本的 webapi接口应用
包括swagger接口管理文档
jwt接口安全认证
aop接口调用轨迹日志
ef映射mysql 使用codefirst模式交互数据库
首先新建一个webapi应用
此次默认配置HTTPS默认是勾选的 此处没用到暂时不进行勾选
可以删除这2个默认的文件
首先我们要建立一个基础的控制器文件
打上路由的标记接口请求地址
继承ControllerBase基类
接下来写一个标准的CRUD 增删查改结构的接口
由于要用ef交互mysql数据库我们先建立一个model文件
table标记为mysql映射的表名,mysql一般表名都为小写
key标记字段为主键
column标记映射mysql表中的字段名称
maxlength为控制字段的最大长度
/// <summary>
/// 测试模型
/// </summary>
[Table("testtable")]
public class TestModel
{
/// <summary>
/// 主键
/// </summary>
[Key]
[Column("id")]
public Guid ID { get; set; }
/// <summary>
/// 名称
/// </summary>
[Display(Name = "名称")]
[Column("name")]
[MaxLength(45)]
public string Name { get; set; }
}
第二步在配置文件中配置mysql的连接地址
首先在Nuget里面安装 Microsoft.EntityFrameworkCore
和Pomelo.EntityFrameworkCore.MySql 包文件
appsettings.json 为生产环境配置文件
Development 为开发环境配置文件
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MySql": "server=localhost;user=root;database=bridgedb;port=3306;password=123456"
},
"Jwt": {
"SecretKey": "wubo199686wubo199686wubo199686",
"Issuer": "WebAppIssuer",
"Audience": "WebAppAudience"
},
"Log4Net": {
"RepositoryName": "NETCoreRepository",
"ConfigFilePath": "Log4Net/log4net.config"
}
}
注册到程序中 修改 program文件
//新增数据库连接读取
builder.Services.AddDbContext<MyDbContext>(options => options.UseMySql(
builder.Configuration.GetConnectionString("MySql"),
ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("MySql"))));
第三步建立数据上下文
public class MyDbContext : DbContext
{
/// <summary>
/// 创建实体
/// </summary>
/// <param name="modelBuilder"></param>
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
}
public MyDbContext(DbContextOptions<MyDbContext> options) : base(options)
{
}
/// <summary>
/// 测试模型
/// </summary>
public DbSet<TestModel> ModelCustom { get; set; }
}
第四步将表结构同步到mysql 生成数据库及表
打开Nuget控制台窗口程序
输入命令
Add-Migration Initial
然后执行
Update-Database
即可同步数据表结构
数据库及ef映射的相关工作都做好了该写我们的CRUD接口了
注入上下文 然后就可以使用linq或者lamda语法操作ef模型了
savechanges就是将更改提交到数据库
/// <summary>
/// api接口
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class TestController : ControllerBase
{
/// <summary>
/// 数据上下文
/// </summary>
private readonly MyDbContext _coreDbContext;
/// <summary>
/// 注入上下文
/// </summary>
/// <param name="coreDbContext"></param>
public TestController(MyDbContext coreDbContext)
{
_coreDbContext = coreDbContext;
}
/// <summary>
/// 查询数据
/// </summary>
/// <returns></returns>
[HttpGet]
public List<TestModel> Get()
{
return _coreDbContext.Set<TestModel>().ToList();
}
/// <summary>
/// 创建数据
/// </summary>
/// <param name="request">测试模型</param>
/// <returns></returns>
[HttpPost]
public void Create([FromBody] TestModel request)
{
_coreDbContext.ModelCustom.Add(request);
_coreDbContext.SaveChanges();
}
/// <summary>
/// 删除数据
/// </summary>
/// <param name="id"></param>
/// <returns></returns>
[HttpDelete]
public void Delete([FromForm] Guid id)
{
var item = _coreDbContext.ModelCustom.Where(b => b.ID == id).FirstOrDefault();
_coreDbContext.ModelCustom.Remove(item);
_coreDbContext.SaveChanges();
}
/// <summary>
/// 更新数据
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
[HttpPut]
public void Update([FromBody] TestModel request)
{
var item = _coreDbContext.ModelCustom.Where(b => b.ID == request.ID).FirstOrDefault();
item.Name = request.Name;
_coreDbContext.SaveChanges();
}
}
接口写好了我们需要使用swagger来对接口做管理和说明
由于6.0是自动继承了swagger的包的所以无需单独配置 而且6.0将startup文件和program文件合并了 我们注册的时候只需要对program文件进行操作即可
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "API",
Description = "API说明"
});
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
});
将此应用属性里面生成 勾选生成api文档
此处运行就成功了
但是接口的话我们要考虑到安全性对于前后端分离的项目来说jwt认证是比较适用的
首先安装Nuget包
System.IdentityModel.Tokens.Jwt
Microsoft.AspNetCore.Authentication.JwtBearer
然后新建一个jwt的帮助类
/// <summary>
/// JWT工具类
/// </summary>
public class JwtHelper
{
private readonly IConfiguration _configuration;
public JwtHelper(IConfiguration configuration)
{
_configuration = configuration;
}
public string CreateToken()
{
// 1. 定义需要使用到的Claims
var claims = new[]
{
new Claim(ClaimTypes.Name, "u_admin"), //HttpContext.User.Identity.Name
new Claim(ClaimTypes.Role, "r_admin"), //HttpContext.User.IsInRole("r_admin")
new Claim(JwtRegisteredClaimNames.Jti, "admin"),
new Claim("Username", "Admin"),
new Claim("Name", "超级管理员")
};
// 2. 从 appsettings.json 中读取SecretKey
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["Jwt:SecretKey"]));
// 3. 选择加密算法
var algorithm = SecurityAlgorithms.HmacSha256;
// 4. 生成Credentials
var signingCredentials = new SigningCredentials(secretKey, algorithm);
// 5. 根据以上,生成token
var jwtSecurityToken = new JwtSecurityToken(
_configuration["Jwt:Issuer"], //Issuer
_configuration["Jwt:Audience"], //Audience
claims, //Claims,
DateTime.Now, //notBefore
DateTime.Now.AddDays(1), //expires
signingCredentials //Credentials
);
// 6. 将token变为string
var token = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
return token;
}
}
配置文件中配置jwt的相关配置
注意SecretKey设置不可过短不然在生成token的时候会有异常 最少16个字符以上
注册到程序中
var builder = WebApplication.CreateBuilder(args);
var configuration = builder.Configuration;
//jwt认证
builder.Services.AddSingleton(new JwtHelper(configuration));
// Add services to the container.
builder.Services.AddControllersWithViews();
builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo
{
Version = "v1",
Title = "API",
Description = "API说明"
});
var xmlFilename = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
options.IncludeXmlComments(Path.Combine(AppContext.BaseDirectory, xmlFilename));
//定义JwtBearer认证方式一
options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
{
Description = "这是方式一(直接在输入框中输入认证信息,不需要在开头添加Bearer)",
Name = "Authorization",//jwt默认的参数名称
In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
Type = SecuritySchemeType.Http,
Scheme = "bearer"
});
//定义JwtBearer认证方式二
//options.AddSecurityDefinition("JwtBearer", new OpenApiSecurityScheme()
//{
// Description = "这是方式二(JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格))",
// Name = "Authorization",//jwt默认的参数名称
// In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)
// Type = SecuritySchemeType.ApiKey
//});
//声明一个Scheme,注意下面的Id要和上面AddSecurityDefinition中的参数name一致
var scheme = new OpenApiSecurityScheme()
{
Reference = new OpenApiReference() { Type = ReferenceType.SecurityScheme, Id = "JwtBearer" }
};
//注册全局认证(所有的接口都可以使用认证)
options.AddSecurityRequirement(new OpenApiSecurityRequirement()
{
[scheme] = new string[0]
});
});
//新增数据库连接读取
builder.Services.AddDbContext<MyDbContext>(options => options.UseMySql(
builder.Configuration.GetConnectionString("MySql"),
ServerVersion.AutoDetect(builder.Configuration.GetConnectionString("MySql"))));
builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true, //是否验证Issuer
ValidIssuer = configuration["Jwt:Issuer"], //发行人Issuer
ValidateAudience = true, //是否验证Audience
ValidAudience = configuration["Jwt:Audience"], //订阅人Audience
ValidateIssuerSigningKey = true, //是否验证SecurityKey
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["Jwt:SecretKey"])), //SecurityKey
ValidateLifetime = true, //是否验证失效时间
ClockSkew = TimeSpan.FromSeconds(30), //过期时间容错值,解决服务器端时间不同步问题(秒)
RequireExpirationTime = true,
};
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
如果全局注册的话 所有接口都需要认证
如果不全局注册的话在需要认证的接口打上标记即可
获取token的方法
/// <summary>
/// 认证控制器
/// </summary>
[Route("api/[controller]/[action]")]
[ApiController]
public class AccountController : ControllerBase
{
private readonly JwtHelper _jwtHelper;
/// <summary>
///
/// </summary>
/// <param name="jwtHelper"></param>
public AccountController(JwtHelper jwtHelper)
{
_jwtHelper = jwtHelper;
}
/// <summary>
/// 获取token
/// </summary>
/// <returns></returns>
[HttpGet]
public ActionResult<string> GetToken()
{
return _jwtHelper.CreateToken();
}
}
加了认证后 不带token请求的话是请求不通的
测试接口的话先将token请求出来
将token输入
再次请求接口即可访问成功
如果是用postman请求的话将token带入请求头即可
现在swagger+jwt+数据库交互都已经完成 剩下最后就是日志了 毕竟接口调用轨迹还是比较重要的 如果是每个接口都写日志的相关代码实在是过于麻烦
此次用log4net+aop自定义属性切面记录日志
首先安装log4net的包
新建log4net相关配置文件
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<log4net>
<!-- 错误日志类-->
<logger name="Error">
<level value="ALL" />
<appender-ref ref="ErrorAppender" />
</logger>
<!-- 错误日志附加介质-->
<appender name="ErrorAppender" type="log4net.Appender.RollingFileAppender">
<!--日志文件路径-->
<param name="File" value="Logs\\Error\\" />
<!--是否是向文件中追加日志-->
<param name="AppendToFile" value="true" />
<!--log保留天数-->
<param name="MaxSizeRollBackups" value="1000" />
<!--最大文件大小-->
<param name="MaxFileSize" value="10240" />
<!--日志文件名是否是固定不变的-->
<param name="StaticLogFileName" value="false" />
<!--日志文件名格式为:2008-08-31.log-->
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<!--日志根据日期滚动-->
<param name="RollingStyle" value="Date" />
<!--信息日志布局-->
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%n==========%n【日志级别】:%-5level%n【记录时间】:%date %n【执行时间】:[%r]毫秒%n%message%n" />
</layout>
</appender>
<!-- 信息日志类 -->
<logger name="Info">
<level value="ALL" />
<appender-ref ref="InfoAppender" />
</logger>
<!-- 信息日志附加介质-->
<appender name="InfoAppender" type="log4net.Appender.RollingFileAppender">
<!--日志文件路径-->
<param name="File" value="Logs\\Info\\" />
<!--是否是向文件中追加日志-->
<param name="AppendToFile" value="true" />
<!--log保留天数-->
<param name="MaxSizeRollBackups" value="100" />
<param name="MaxFileSize" value="1" />
<!--日志文件名是否是固定不变的-->
<param name="StaticLogFileName" value="false" />
<!--日志文件名格式为:2008-08-31.log-->
<param name="DatePattern" value="yyyy-MM-dd.'log'" />
<!--日志根据日期滚动-->
<param name="RollingStyle" value="Date" />
<!--信息日志布局-->
<layout type="log4net.Layout.PatternLayout">
<param name="ConversionPattern" value="%n==========%n【日志级别】:%-5p%n【记录时间】:%d [%t]%n【信息详情】:%m%n" />
</layout>
</appender>
</log4net>
</configuration>
public class Log4NetConfig
{
public static string RepositoryName { get; set; }
public static void Init(IConfiguration configuration)
{
var repositoryName = configuration.GetSection("Log4Net:RepositoryName").Value;
if (string.IsNullOrWhiteSpace(repositoryName))
{
throw new Exception("必须在配置文件中添加 Log4Net > RepositoryName 节点");
}
RepositoryName = repositoryName;
var configFilePath = configuration.GetSection("Log4Net:ConfigFilePath").Value;
if (string.IsNullOrWhiteSpace(configFilePath))
{
configFilePath = "log4net.config";
}
var file = new FileInfo(configFilePath);
var repository = LogManager.CreateRepository(repositoryName);
XmlConfigurator.Configure(repository, file);
}
}
新增一个LogFilter的过滤器
/// <summary>
/// 日志过滤器
/// </summary>
public class LogFilter : ActionFilterAttribute
{
private static readonly ILog InfoLog = LogManager.GetLogger(Log4NetConfig.RepositoryName, "Info");
/// <summary>
/// Action方法调用之前执行
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuting(ActionExecutingContext context)
{
var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
string param = string.Empty;
string globalParam = string.Empty;
foreach (var arg in context.ActionArguments)
{
string value = JsonConvert.SerializeObject(arg.Value);
param += $"{arg.Key} : {value} \r\n";
globalParam += value;
}
InfoLog.Info($"webapi方法名称:【{descriptor.ActionName}】接收到参数为:{param}");
}
/// <summary>
/// Action 方法调用后,Result 方法调用前执行
/// </summary>
/// <param name="context"></param>
public override void OnActionExecuted(ActionExecutedContext context) { }
/// <summary>
/// Result 方法调用前执行
/// </summary>
/// <param name="context"></param>
public override void OnResultExecuting(ResultExecutingContext context) { }
/// <summary>
/// Result 方法调用后执行
/// </summary>
/// <param name="context"></param>
public override void OnResultExecuted(ResultExecutedContext context)
{
var descriptor = context.ActionDescriptor as ControllerActionDescriptor;
string result = string.Empty;
if (context.Result is ObjectResult)
{
result = Newtonsoft.Json.JsonConvert.SerializeObject(((ObjectResult)context.Result).Value);
}
InfoLog.Info($"webapi方法名称【{descriptor.ActionName}】执行的返回值 : {result}");
}
}
然后注册到program
需要记录日志的在接口打上[LogFilter]标记即可
此次从零到一搭建netcore6.0 webapi+swagger+jwt认证+aop日志+mysql codefirst模式就已经完成 希望可以和大家共同交流学习