从零到一搭建netcore6.0 webapi+swagger+jwt认证+aop日志+mysql codefirst模式

本文介绍从零到一搭建基于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模式就已经完成 希望可以和大家共同交流学习

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值