06 文宇通用管理平台敏捷开发--.NET6后台

文宇通用管理平台敏捷开发–.NET6后台

大家好,

1、 开发环境配置

VS2022 IIS .NET6

VSCode Nodejs ElementPlus

SqlServer数据库

2、后端服务搭建

2.1 创建解决方案

Wenyu.Framework.PortalProject

2.2 创建CoreWebApi项目

Wenyu.Framework.WebApi

3、配置Log4Net

3.1 nuget引入:

<PackageReference Include="log4net" Version="2.0.15" />
<PackageReference Include="Microsoft.Extensions.Logging.Log4Net.AspNetCore" Version="6.1.0" />
<PackageReference Include="System.Data.SqlClient" Version="4.8.5" />

3.2 定义配置文件log4net.config

CfgFiles/log4net.config

<?xml version="1.0" encoding="utf-8" ?>
<log4net>
	<!-- Define some output appenders -->
	<appender name="rollingAppender" type="log4net.Appender.RollingFileAppender">
		<file value="log4\log.txt" />
		<!--追加日志内容-->
		<appendToFile value="true" />

		<!--防止多线程时不能写Log,官方说线程非安全-->
		<lockingModel type="log4net.Appender.FileAppender+MinimalLock" />

		<!--可以为:Once|Size|Date|Composite-->
		<!--Composite为Size和Date的组合-->
		<rollingStyle value="Composite" />

		<!--当备份文件时,为文件名加的后缀,这里可以作为每一天的日志分别存储不同的文件-->
		<datePattern value="yyyyMMdd&quot;.txt&quot;" />
		<StaticLogFileName value="false"/>

		<!--日志最大个数,都是最新的-->
		<!--rollingStyle节点为Size时,只能有value个日志-->
		<!--rollingStyle节点为Composite时,每天有value个日志-->
		<maxSizeRollBackups value="20" />

		<!--可用的单位:KB|MB|GB-->
		<maximumFileSize value="3MB" />

		<!--置为true,当前最新日志文件名永远为file节中的名字-->
		<staticLogFileName value="true" />

		<!--输出级别在INFO和ERROR之间的日志-->
		<filter type="log4net.Filter.LevelRangeFilter">
			<param name="LevelMin" value="ALL" />
			<param name="LevelMax" value="FATAL" />
		</filter>

		<layout type="log4net.Layout.PatternLayout">
			<!--日志输出格式:时间  日志类型  日志内容-->
			<conversionPattern value="%date [%thread] %-5level %logger - %message%newline"/>
		</layout>
	</appender>

	<!--数据库日志-->
	<!--https://blog.csdn.net/liyou123456789/article/details/126264613-->
	<appender name="AdoNetAppender_SqlServer" type="log4net.Appender.AdoNetAppender">
		<!--日志缓存写入条数,设置为0时只要有一条就立刻写到数据库-->
		<bufferSize value="0" />
		
		<connectionType value="System.Data.SqlClient.SqlConnection,System.Data.SqlClient, Version=4.6.1.5, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
		<connectionString value="data source=.;initial catalog=Wenyu.Framework.DB;integrated security=false;persist security info=True;User ID=sa;Password=123456;TrustServerCertificate=True" />
		<commandText value="INSERT INTO Log ([Date],[Thread],[Level],[Logger],[Message],[Exception]) VALUES (@log_date, @thread, @log_level, @logger, @message, @exception)" />
		<parameter>
			<parameterName value="@log_date" />
			<dbType value="DateTime" />
			<layout type="log4net.Layout.RawTimeStampLayout" />
		</parameter>
		<parameter>
			<parameterName value="@thread" />
			<dbType value="String" />
			<size value="255" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%thread" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@log_level" />
			<dbType value="String" />
			<size value="50" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%level" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@logger" />
			<dbType value="String" />
			<size value="255" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%logger" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@message" />
			<dbType value="String" />
			<size value="4000" />
			<layout type="log4net.Layout.PatternLayout">
				<conversionPattern value="%message" />
			</layout>
		</parameter>
		<parameter>
			<parameterName value="@exception" />
			<dbType value="String" />
			<size value="2000" />
			<layout type="log4net.Layout.ExceptionLayout" />
		</parameter>
	</appender>


	<!-- levels: OFF > FATAL > ERROR > WARN > INFO > DEBUG  > ALL -->
	<root>
		<priority value="ALL"/>
		<!--在系统的日志筛选之后又进行筛选-->
		<level value="INFO"/>
		<appender-ref ref="rollingAppender" />
		<appender-ref ref="AdoNetAppender_SqlServer" />
	</root>
</log4net>



builder.Logging.AddLog4Net("CfgFiles/log4net.config");

3.3 创建日志表Log

数据库Wenyu.Framework.DB的日志表Log

create table Log (
   Datetime timestamp(3),
   Thread varchar2(255),
   Log_Level varchar2(255),
   Logger varchar2(255),
   Message varchar2(4000)
   );

4、Swagger配置

项目创建之时,启用OpenApi默认生成

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();


if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

4.1、 支持注释

4.1.1 选中CoreWebApi–属性–生成–输出–文档生成–勾选生成包含项目公共API的引用程序集

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    //支持多版本
    foreach(string version in typeof(ApiVersions).GetEnumNames())
    {
        options.SwaggerDoc(version,new OpenApiInfo()
        {
            Title = $"文宇敏捷后台管理项目Api文档",
            Version = version,
            Description = $"通用版本的CoreApi版本--{version}"
        });                    
    }

    //xml文档绝对路径
    var file = Path.Combine(AppContext.BaseDirectory, $"{AppDomain.CurrentDomain.FriendlyName}.xml");
    //true:显示控制器层注释
    options.IncludeXmlComments(file, true);
    //对action的名称进行排序,如果有多个,就可以看见效果
    options.OrderActionsBy(o => o.RelativePath);

    #region 支持jwt token授权
    //添加安全定义
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey, //jwt默认的参数名称
        BearerFormat = "JWT",
        Scheme = "Bearer"
    });

    #endregion
    //添加安全要求
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference=new OpenApiReference()
                {
                    Type=ReferenceType.SecurityScheme,
                    Id="Bearer"
                }
            },
            new string[]{}
        }
    });
});

4.2 支持版本控制

4.2.1 添加版本枚举

ApiVersions.cs

namespace Wenyu.Framework.WebApi
{
    /// <summary>
    /// Api版本枚举
    /// </summary>
    public enum ApiVersions
    {
        V1,
        V2,
        V3,
        V4
    }
}

4.3 支持jwt token授权

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(options =>
{
    //支持多版本
    foreach(string version in typeof(ApiVersions).GetEnumNames())
    {
        options.SwaggerDoc(version,new OpenApiInfo()
        {
            Title = $"文宇敏捷后台管理项目Api文档",
            Version = version,
            Description = $"通用版本的CoreApi版本--{version}"
        });                    
    }

    //xml文档绝对路径
    var file = Path.Combine(AppContext.BaseDirectory, $"{AppDomain.CurrentDomain.FriendlyName}.xml");
    //true:显示控制器层注释
    options.IncludeXmlComments(file, true);
    //对action的名称进行排序,如果有多个,就可以看见效果
    options.OrderActionsBy(o => o.RelativePath);

    #region 支持jwt token授权
    //添加安全定义
    options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
    {
        Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
        Name = "Authorization",
        In = ParameterLocation.Header,
        Type = SecuritySchemeType.ApiKey, //jwt默认的参数名称
        BearerFormat = "JWT",
        Scheme = "Bearer"
    });

    #endregion
    //添加安全要求
    options.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        {
            new OpenApiSecurityScheme
            {
                Reference=new OpenApiReference()
                {
                    Type=ReferenceType.SecurityScheme,
                    Id="Bearer"
                }
            },
            new string[]{}
        }
    });
});

4.4 抽离到独立项目

Wenyu.Framework.WebCore

<ItemGroup>
  <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
  <PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
</ItemGroup>

#SwaggerExtend/ApiVersions.cs

namespace Wenyu.Framework.WebCore.SwaggerExtend
{
    /// <summary>
    /// Api版本枚举
    /// </summary>
    public enum ApiVersions
    {
        V1,
        V2,
        V3,
        V4
    }
}

#SwaggerExtend/SwaggerExtension.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace Wenyu.Framework.WebCore.SwaggerExtend
{
    public static class SwaggerExtension
    {
        /// <summary>
        /// 配置swagger的配置
        /// </summary>
        /// <param name="service"></param>
        public static void AddSwaggerExt(this IServiceCollection service,string docName="",string docDesc= "")
        {
            service.AddEndpointsApiExplorer();
            service.AddSwaggerGen(options =>
            {
                //支持多版本
                foreach (string version in typeof(ApiVersions).GetEnumNames())
                {
                    options.SwaggerDoc(version, new OpenApiInfo()
                    {
                        Title =!string.IsNullOrWhiteSpace(docName)?docName: $"文宇敏捷后台管理项目Api文档",
                        Version = version,
                        Description = !string.IsNullOrWhiteSpace(docDesc) ? docDesc : $"通用版本的CoreApi版本--{version}"
                    });
                }

                //xml文档绝对路径
                var file = Path.Combine(AppContext.BaseDirectory, $"{AppDomain.CurrentDomain.FriendlyName}.xml");
                //true:显示控制器层注释
                options.IncludeXmlComments(file, true);
                //对action的名称进行排序,如果有多个,就可以看见效果
                options.OrderActionsBy(o => o.RelativePath);

                #region 支持jwt token授权
                //添加安全定义
                options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
                {
                    Description = "JWT授权(数据将在请求头中进行传输) 在下方输入Bearer {token} 即可,注意两者之间有空格",
                    Name = "Authorization",
                    In = ParameterLocation.Header,
                    Type = SecuritySchemeType.ApiKey, //jwt默认的参数名称
                    BearerFormat = "JWT",
                    Scheme = "Bearer"
                });

                #endregion
                //添加安全要求
                options.AddSecurityRequirement(new OpenApiSecurityRequirement
                {
                    {
                        new OpenApiSecurityScheme
                        {
                            Reference=new OpenApiReference()
                            {
                                Type=ReferenceType.SecurityScheme,
                                Id="Bearer"
                            }
                        },
                        new string[]{}
                    }
                });
            });
        }

        /// <summary>
        /// 使用
        /// </summary>
        /// <param name="app"></param>
        public static void UseSwaggerExt(this WebApplication app,string docName = "")
        {
            app.UseSwagger();
            app.UseSwaggerUI(options =>
            {
                foreach (string version in typeof(ApiVersions).GetEnumNames())
                {
                    options.SwaggerEndpoint($"/swagger/{version}/swagger.json", !string.IsNullOrWhiteSpace(docName) ? docName : $"文宇敏捷后台管理项目Api文档【{version}】版本");
                }
            });
        }
    }
}

使用

builder.Services.AddSwaggerExt();

if (app.Environment.IsDevelopment())
{
    //app.UseSwaggerExt("文宇敏捷后台管理项目Api文档");
    app.UseSwaggerExt();
}

5、支持跨域请求

5.1 配置跨域拓展方法

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace Wenyu.Framework.WebCore.CorsExtend
{
    public static class CorsExtension
    {
        /// <summary>
        /// 配置跨域
        /// </summary>
        /// <param name="service"></param>
        public static void AddCorsExt(this IServiceCollection service)
        {
            service.AddCors(options =>
            {
                options.AddPolicy("allCors", policy =>
                {
                    policy.AllowAnyHeader()
                        .AllowAnyOrigin()
                        .AllowAnyMethod();
                });
            });
        }
        /// <summary>
        /// 使用跨域策略生效
        /// </summary>
        /// <param name="app"></param>
        public static void UseCorsExt(this WebApplication app)
        {
            app.UseCors("allCors");
        }
    }
}

5.2 使用跨域

builder.Services.AddCorsExt();
app.UseCorsExt();

6、配置支持EfCore

6.1 nuget引入


<PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.25" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="6.0.25" />

6.2 增加数据库访问层

Wenyu.Framework.DbModels

增加实体

Entities/UserEntity.cs

namespace Wenyu.Framework.DbModels.Entities
{
    public class UserEntity
    {
        public int UserId { get; set; }
        public string Name { get; set; }
        public int Age { get; set; }
    }
}

6.3 准备DbContext

EFCore操作数据库的核心

FrameworkDbContext.cs

using Microsoft.EntityFrameworkCore;
using Wenyu.Framework.DbModels.Entities;

namespace Wenyu.Framework.DbModels
{
    public class FrameworkDbContext:DbContext
    {
        private readonly string _connectionString;

        /// <summary>
        /// 带有书库连接字符串的参数
        /// </summary>
        /// <param name="connectionString"></param>
        public FrameworkDbContext(string connectionString) {
            this._connectionString = connectionString;
        }

        public FrameworkDbContext(DbContextOptions<FrameworkDbContext> options)
            :base(options)
        {
            
        }

        public virtual DbSet<UserEntity> UserEntities { get; set; }

        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(this._connectionString);
            }
        }

        /// <summary>
        /// 配置数据库表映射关系
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<UserEntity>(entity =>
            {
                entity.ToTable("UserEntity")
                .HasKey(u=>u.UserId);
            });
        }
    }
}

6.4 使用控制台程序初始化数据

Wenyu.Framework.InitDatabase

using Wenyu.Framework.DbModels;

namespace Wenyu.Framework.InitDatabase
{
    internal class Program
    {
        static void Main(string[] args)
        {
            string connString = "Data Source=.;Initial Catalog=Wenyu.Framework.DB;User ID=sa;Password=123456";
            

            using(FrameworkDbContext context=new FrameworkDbContext(connString))
            {
                //根据数据库连接字符串的配置,如果不存在就不操作
                context.Database.EnsureDeleted();
                //根据数据库连接字符串的配置,如果存在就不创建
                context.Database.EnsureCreated();
            }

            Console.WriteLine("数据库创建成功");
        }
    }
}

6.5 依赖注入DbContext

#region DbContext
var connString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddDbContext<FrameworkDbContext>(optionsBuilder =>
{
    optionsBuilder.UseSqlServer(connString);
});
#endregion

7 构建业务逻辑层

7.1 为支持IOC容器,这创建的业务逻辑层有抽象层和具体实现层

7.2 抽象层

Wenyu.Framework.IBusinessServices

7.3 具体实现层

Wenyu.Framework.BusinessServices

7.4 创建基础接口(通用接口)

IBaseService ,增加分页实体:PagingData

7.4.1 IBaseService.cs
using System.Data.SqlClient;
using System.Linq.Expressions;

namespace Wenyu.Framework.IBusinessServices
{
    public interface IBaseService
    {
        #region 查询
        /// <summary>
        /// 主键查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public T Find<T>(int id) where T : class;

        /// <summary>
        /// 不应该暴露给上端使用者,尽量少用
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        /// [Obsolete("尽量避免使用,使用带表达式目录树的代替")]
        public IQueryable<T> Set<T>() where T : class;

        /// <summary>
        /// 根据表达式目录树条件查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="funcWhere"></param>
        /// <returns></returns>
        public IQueryable<T> Query<T>(Expression<Func<T,bool>> funcWhere) where T : class;

        /// <summary>
        /// 分页查询
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <typeparam name="S"></typeparam>
        /// <param name="funcWhere"></param>
        /// <param name="pageSize"></param>
        /// <param name="pageIndex"></param>
        /// <param name="funcOrderBy"></param>
        /// <param name="isAsc"></param>
        /// <returns></returns>
        public PagingData<T> QueryPage<T,S>(Expression<Func<T, bool>> funcWhere,
            int pageSize,int pageIndex,Expression<Func<T,S>> funcOrderBy,bool isAsc=true) where T : class;
        #endregion

        #region 添加
        /// <summary>
        /// 即时保存,不需要再Commit
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public T Insert<T>(T t) where T : class;

        /// <summary>
        /// 新增集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tList"></param>
        /// <returns></returns>
        public IEnumerable<T> Insert<T>(IEnumerable<T> tList) where T : class;

        #endregion

        #region 修改
        /// <summary>
        /// 修改一个对象
        /// 如果已经在Context,只能再封装一个
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public int Update<T>(T t) where T : class;

        /// <summary>
        /// 修改一个集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public int Update<T>(IEnumerable<T> tList) where T : class;
        #endregion

        #region 删除
        /// <summary>
        /// 删除一个对象
        /// 先附加再删除
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="t"></param>
        /// <returns></returns>
        public int Delete<T>(T t) where T : class;

        /// <summary>
        /// 根据主键删除记录
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="id"></param>
        /// <returns></returns>
        public int Delete<T>(int id) where T : class;

        /// <summary>
        /// 删除一个对象集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="tList"></param>
        /// <returns></returns>
        public int Delete<T>(IEnumerable<T> tList) where T : class;
        #endregion

        #region Other
        /// <summary>
        /// 执行sql语句,返回IQueryable
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class;
        
        /// <summary>
        /// 执行sql语句
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="sql"></param>
        /// <param name="parameters"></param>
        /// <returns></returns>
        public void Excute<T>(string sql, SqlParameter[] parameters) where T : class;

        #endregion

        #region 伪代码

        #endregion
    }
}
7.4.2 BaseService.cs
<ItemGroup>
  <PackageReference Include="Microsoft.EntityFrameworkCore" Version="6.0.25" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.Relational" Version="6.0.25" />
</ItemGroup>


using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using System.Data.SqlClient;
using System.Linq.Expressions;
using Wenyu.Framework.IBusinessServices;

namespace Wenyu.Framework.BusinessServices
{
    public abstract class  BaseService: IBaseService
    {
        protected DbContext Context { get; set; }
        /// <summary>
        /// 构造函数注入
        /// </summary>
        /// <param name="context"></param>
        public BaseService(DbContext context)
        {
            Context=context;
        }

        #region Query 查询
        public IQueryable<T> Set<T>() where T : class
        {
            return this.Context.Set<T>();
        }

        public T Find<T>(int id) where T : class
        {
            return this.Context.Set<T>().Find(id);
        }

        public IQueryable<T> Query<T>(Expression<Func<T, bool>> funcWhere) where T : class
        {
            return this.Context.Set<T>().Where<T>(funcWhere);
        }

        public PagingData<T> QueryPage<T, S>(Expression<Func<T, bool>> funcWhere, int pageSize, int pageIndex, Expression<Func<T, S>> funcOrderBy, bool isAsc = true) where T : class
        {
            var list=Set<T>();
            if (funcWhere != null)
            {
                list=list.Where(funcWhere);
            }
            if (isAsc)
            {
                list = list.OrderBy(funcOrderBy);
            }
            else
            {
                list = list.OrderByDescending(funcOrderBy);
            }
            PagingData<T> result = new PagingData<T>()
            {
                DataList = list.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(),
                RecordCount = list.Count(),
                PageIndex=pageIndex,
                PageSize=pageSize,
            };
            return result;

        }

        

        

        #endregion

        #region Insert 添加

        public T Insert<T>(T t) where T : class
        {
            this.Context.Set<T>().Add(t);
            this.Commit();
            return t;
        }

        public IEnumerable<T> Insert<T>(IEnumerable<T> tList) where T : class
        {
            this.Context.Set<T>().AddRange(tList);
            this.Commit();
            return tList;
        }

        #endregion

        #region Update 修改

        public int Update<T>(T t) where T : class
        {
            this.Context.Set<T>().Attach(t);
            this.Context.Entry<T>(t).State = EntityState.Modified;
            return this.Commit();
        }

        public int Update<T>(IEnumerable<T> tList) where T : class
        {
            foreach (var t in tList)
            {
                this.Context.Set<T>().Attach(t);
                this.Context.Entry<T>(t).State=EntityState.Modified;
            }
            return this.Commit ();
        }

        #endregion

        #region Delete 删除
        public int Delete<T>(T t) where T : class
        {
            if (t == null) throw new ArgumentNullException("this is null");
            this.Context.Set<T>().Attach(t);
            this.Context.Set<T>().Remove(t);
            return this.Commit();
        }

        public int Delete<T>(int id) where T : class
        {
            T t=this.Find<T>(id);
            if (t == null) throw new ArgumentNullException("this is null");
            this.Context.Set<T>().Remove(t);
            return this.Commit();
        }

        public int Delete<T>(IEnumerable<T> tList) where T : class
        {
            foreach(var t in tList)
            {
                this.Context.Set<T>().Attach(t);
            }
            
            this.Context.Set<T>().RemoveRange(tList);
            return this.Commit();
        }

        #endregion


        #region Other
        public void Excute<T>(string sql, SqlParameter[] parameters) where T : class
        {
            IDbContextTransaction transaction = null;
            try
            {
                transaction = this.Context.Database.BeginTransaction();
                this.Context.Database.ExecuteSqlRaw(sql, parameters);
                transaction.Commit();
            }catch (Exception ex)
            {
                if(transaction != null)
                    transaction.Rollback();
                throw;
            }

        }

        public IQueryable<T> ExcuteQuery<T>(string sql, SqlParameter[] parameters) where T : class
        {
            return this.Context.Set<T>().FromSqlRaw(sql, parameters);
        }

        
        #endregion

        protected int Commit()
        {
            return this.Context.SaveChanges();
        }
        #region 伪代码

        #endregion

        public virtual void Dispose()
        {
            if(this.Context != null)
            {
                this.Context.Dispose();
            }
        }

    }
}
7.4.3 PagingData.cs

namespace Wenyu.Framework.IBusinessServices
{
    public class PagingData<T> where T : class
    {
        public int RecordCount { get; set; }
        public int PageIndex { get; set; }
        public int PageSize { get; set; }
        public List<T>? DataList { get; set; }
        public string? SearchString { get; set; }
    }
}

7.4.4 IUserService.cs
public interface IUserService:IBaseService
{

}

7.4.4 UserService.cs
using Wenyu.Framework.DbModels;
using Wenyu.Framework.IBusinessServices;

namespace Wenyu.Framework.BusinessServices
{
    public class UserService : BaseService, IUserService
    {
        public UserService(FrameworkDbContext context) : base(context)
        {
        }
    }
}
7.4.5 配置依赖注入服务

Program.cs

"ConnectionStrings": {
  "Default": "Data Source=.;Initial Catalog=Wenyu.Framework.DB;User ID=sa;Password=123456"
}


var connString = builder.Configuration.GetConnectionString("Default");
builder.Services.AddDbContext<FrameworkDbContext>(optionsBuilder =>
{
    optionsBuilder.UseSqlServer(connString);
});

builder.Services.AddTransient<IUserService, UserService>();

8、 autofac支持

8.1 引入nuget包

<ItemGroup>
    <PackageReference Include="Autofac" Version="7.1.0" />
    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="8.0.0" />
  </ItemGroup>

8.2 添加autofac模块化配置代码

创建一个测试服务
namespace WorldSystem.Api.Common.Services
{
    public class TodayTestService
    {
        public string ShowTime()
        {
            string time = DateTime.Now.ToString();
            Console.WriteLine(time);
            return time;
        }
    }
}

在autofac模块配置中注册服务

using Autofac;
using System.Reflection;
using WorldSystem.Api.Common.Services;

namespace WorldSystem.Api.Configs
{
    public class AutofacModuleConfig:Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // 批量注册服务
            //Assembly interfaces = Assembly.Load("Interfaces");
            //Assembly services = Assembly.Load("Services");
            //builder.RegisterAssemblyTypes(interfaces, services).AsImplementedInterfaces();

            // 注册服务
            builder.RegisterType<TodayTestService>();

        }
    }
}

8.3 替换aspnetcore自带容器

在Program.cs上添加如下代码:

#region 替换为autofac依赖注入容器
            builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory())
            .ConfigureContainer<ContainerBuilder>(containerBuilder =>
            {
                containerBuilder.RegisterModule(new AutofacModuleConfig());
            });
            #endregion

9 Autofac支持Aop

两种方式:

1 通过接口实现Aop拓展,拓展后,实现该接口的所有方法都会支持AOP拓展功能

2 通过类实现Aop拓展,拓展后,定义为虚方法的实现方法支持AOP拓展

二者比较:通过类实现更加灵活,可以选择行支持AOP

9.1 nuget包引入

<ItemGroup>
  <PackageReference Include="Castle.Core" Version="5.1.1" />
  <PackageReference Include="Autofac.Extras.DynamicProxy" Version="7.1.0" />
</ItemGroup>

9.2 拓展AOP写日志

创建项目:Wenyu.Framework.AutofacAOP

AOPExtend/CustomLogInterceptor.cs

using Castle.DynamicProxy;
using Microsoft.Extensions.Logging;

namespace Wenyu.Framework.AutofacAOP.AOPExtend
{
    public class CustomLogInterceptor : IInterceptor  //nuget:Castle.Core
    {
        private readonly ILogger<CustomLogInterceptor> _logger;

        public CustomLogInterceptor(ILogger<CustomLogInterceptor> logger)
        {
            this._logger = logger;
        }
        public void Intercept(IInvocation invocation)
        {
            _logger.LogInformation($"===================={invocation.Method.Name}执行前=================");
            invocation.Proceed();//继续往后执行
            _logger.LogInformation($"===================={invocation.Method.Name}执行后=================");

        }
    }
}

9.3 通过接口实现Aop拓展

using Autofac.Extras.DynamicProxy;
using Wenyu.Framework.AutofacAOP.AOPExtend;

namespace Wenyu.Framework.IBusinessServices
{
    [Intercept(typeof(CustomLogInterceptor))]  //nuget:Autofac.Extras.DynamicProxy
    public interface ITestService
    {
        public void ShowName();
        
    }
}

builder.RegisterType<TestService>().As<ITestService>()
    .EnableInterfaceInterceptors(); //nuget:Autofac.Extras.DynamicProxy

9.4 通过类实现Aop拓展

using Autofac.Extras.DynamicProxy;
using Wenyu.Framework.AutofacAOP.AOPExtend;
using Wenyu.Framework.DbModels;
using Wenyu.Framework.IBusinessServices;

namespace Wenyu.Framework.BusinessServices
{
    [Intercept(typeof(CustomLogInterceptor))]  //nuget:Autofac.Extras.DynamicProxy
    public class TestService :ITestService
    {
        public TestService(FrameworkDbContext context)
        {
        }

        //非virtual的无法进入Aop代理
        public void ShowName()
        {

        }
        //virtual的能有Aop代理
        public virtual void ShowAndName()
        {

        }
    }
}
builder.RegisterType<TestService>().As<ITestService>()
    //.EnableInterfaceInterceptors(); //nuget:Autofac.Extras.DynamicProxy
    .EnableClassInterceptors(); //nuget:Autofac.Extras.DynamicProxy

10、 Api返回统一格式

10.1 定义通用的返回结果类ApiResult.cs

ApiResult.cs

namespace Wenyu.Framework.Common
{
    /// <summary>
    /// api json 通用返回格式
    /// </summary>
    public class ApiResult
    {
        /// <summary>
        /// 是否正常返回
        /// </summary>
        public bool Success { get; set; }
        /// <summary>
        /// 消息
        /// </summary>
        public string? Message { get; set; }
    }

    public class ApiDataResult<T>: ApiResult
    {
        /// <summary>
        /// 结果集数据
        /// </summary>
        public T? Data { get; set; }

        /// <summary>
        /// 冗余结果
        /// </summary>
        public object? OValue { get; set; }
    }
}

10.2 使用方法

[HttpGet]
public IActionResult GetUsers()
{
   var list= _userService.Set<UserEntity>().AsQueryable().ToList();
    return new JsonResult(new ApiDataResult<List<UserEntity>>
    {
        Success = true,
        Message = "成功",
        Data = list
    }) ;
}

11、AutoMapper支持

11.1 安装AutoMapper库

在ASP.NET Core项目中,使用NuGet包管理器安装AutoMapper库,或者在Package Manager Console中运行以下命令:

Install-Package AutoMapper
<ItemGroup>
    <PackageReference Include="AutoMapper" Version="12.0.1" />
    <PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="12.0.1" />
  </ItemGroup>

11.2 创建映射配置

在项目的根目录下创建一个映射配置类,该类将定义对象之间的映射关系。例如,以下是一个简单的映射配置类:

using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.ModelDto;

namespace Wenyu.Framework.WebCore.AutoMapperExtend
{
    public class WebApiAutoMapperProfile:AutoMapper.Profile
    {
        public WebApiAutoMapperProfile()
        {
            CreateMap<UserEntity, UserDto>()
                .ForMember(c=>c.UserId,s=>s.MapFrom(x=>x.UserId))
                .ForMember(c => c.Name, s => s.MapFrom(x => x.Name))
                .ReverseMap();
            //CreateMap<UserInfoDto, LoginDto>().ReverseMap();
            //CreateMap<UserInfo, UserInfoDto>().ReverseMap();
            //CreateMap<UserInfoInput, UserInfo>();
        }
    }
}


11.3 在启动代码中注册映射配置

在ASP.NET Core的启动代码(通常是Startup.cs文件)中注册映射配置。在Startup类的构造函数中添加以下代码:

 #region Automapper
 builder.Services.AddAutoMapper(typeof(WebApiAutoMapperProfile));
 #endregion
 

或者
public Startup(IConfiguration configuration)
{
    Configuration = configuration;
}

public IConfiguration Configuration { get; }

public void ConfigureServices(IServiceCollection services)
{
    // Add other services here...

    services.AddAutoMapper(typeof(WebApiAutoMapperProfile);
}

在上面的代码中,我们使用AddAutoMapper方法将映射配置注册到服务容器中。

11.4 BusinessServices业务层中使用

现在可以在需要的地方使用AutoMapper进行数据转换。例如,在业务服务中可以使用以下代码将领域模型转换为视图模型:

using AutoMapper;
using SqlSugar;
using WorldSystem.Api.Common.Db;
using WorldSystem.Api.Models.Entities;
using WorldSystem.Api.Models.Services.User.Dto;

namespace WorldSystem.Api.Models.Services.User
{
    public class UserInfoService : IUserInfoService
    {
        private readonly ISqlSugarClient _db;
        private readonly IMapper _mapper;

        public UserInfoService(ISqlSugarClient db ,IMapper mapper)
        {
            this._db = db;
            this._mapper = mapper;
        }

        public async Task<UserInfoDto> CheckLogin(LoginDto loginDto)
        {
            UserInfo userInfo = await _db.Queryable<UserInfo>().FirstAsync(p => p.QQ.Equals(loginDto.QQ) && p.Password.Equals(loginDto.Password));
            UserInfoDto userInfoDto=_mapper.Map<UserInfoDto>(userInfo);
            return userInfoDto;
        }
    }
}

11.5 Controllers控制器中使用

private readonly IMapper _mapper;

public TestController(ILogger<TestController> logger, IUserService userService,IMapper mapper)
{
    _logger = logger;
    _userService = userService;
    this._mapper = mapper;   
}

[HttpGet]
public IActionResult GetUsersDto()
{
    var userList = _userService.Set<UserEntity>().AsQueryable().ToList();
    var list =_mapper.Map<List<UserDto>>(userList);
    return new JsonResult(new ApiDataResult<List<UserDto>>
    {
        Success = true,
        Message = "成功",
        Data = list
    });
}

12 基于角色权限系统功能实现

12.1 基础类

12.2 枚举类型


namespace Wenyu.Framework.Common.EnumTypes
{
    public enum UserTypeEnum
    {
        /// <summary>
        /// 管理员
        /// </summary>
        Administrator=1,
        /// <summary>
        /// 普通用户
        /// </summary>
        GeneralUser=2
    }
}


namespace Wenyu.Framework.Common.EnumTypes
{
    public enum StatusEnum
    {
        /// <summary>
        /// 正常
        /// </summary>
        Normal=0,
        /// <summary>
        /// 冻结
        /// </summary>
        Frozen=1,
        /// <summary>
        /// 已删除
        /// </summary>
        Delete=2

    }
}

12.3 权限实体

using Wenyu.Framework.Common.EnumTypes;

namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 基础类
    /// </summary>
    public abstract class BaseEntity
    {
        /// <summary>
        /// 创建时间
        /// </summary>
        public DateTime CreateTime { get; set; } = DateTime.Now;
        /// <summary>
        /// 修改时间
        /// </summary>
        public DateTime ModifyTime { get; set; }
        /// <summary>
        /// 状态 0正常 1冻结 2 删除
        /// </summary>
        public StatusEnum Status { get; set; }
    }
}
using Wenyu.Framework.Common.EnumTypes;

namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 用户信息
    /// </summary>
    public class UserEntity:BaseEntity
    {
        /// <summary>
        /// 用户唯一id
        /// </summary>
        public int UserId { get; set; }
        /// <summary>
        /// 用户名
        /// </summary>
        public string? UserName { get; set; }
        /// <summary>
        /// 姓名
        /// </summary>
        public string? Name { get; set; }
        /// <summary>
        /// 密码
        /// </summary>
        public string? Password { get; set; }
        /// <summary>
        /// 年龄
        /// </summary>
        public int Age { get; set; }
        /// <summary>
        /// 用户类型-UserTypeEnum
        /// 1:管理员  系统默认生成
        /// 2:普通用户 添加的或者注册的用户都为普通用户
        /// </summary>
        public UserTypeEnum UserType { get; set; }
        /// <summary>
        /// 电话
        /// </summary>
        public string? Phone { get; set; }
        /// <summary>
        /// 移动电话
        /// </summary>
        public string? Mobile { get; set; }

        /// <summary>
        /// 性别  0男 1女
        /// </summary>
        public int Sex { get; set; }
        /// <summary>
        /// 微信
        /// </summary>
        public string? Wechat { get; set; }
        /// <summary>
        /// QQ
        /// </summary>
        public string? QQ { get; set; }
        /// <summary>
        /// 邮箱
        /// </summary>
        public string? Email { get; set; }
        /// <summary>
        /// 头像
        /// </summary>
        public string? ImageUrl { get; set; }
        /// <summary>
        /// 最后登录时间
        /// </summary>
        public DateTime? LastLoginTime { get; set; }
    }
}
namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 角色信息
    /// </summary>
    public class RoleEntity:BaseEntity
    {
        public int RoleId { get; set; }
        public int RoleName { get; set; }
    }
}
namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 菜单信息
    /// </summary>
    public class MenuEntity:BaseEntity
    {
        public Guid Id { get; set; }

        public Guid? ParentId { get; set; }
        public string? MenuText { get; set; }
        public int MenuType { get; set; }
        public string? Icon { get; set; }
        public string? WebUrlName { get; set; }
        public string? WebUrl { get; set; }
        public string? VueFilePath { get; set; }
        public bool IsLeafNode { get; set; }
        public int OrderBy { get; set; }
    }
}
namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 用户角色关系信息
    /// </summary>
    public class UserRoleMapEntity:BaseEntity
    {
        public int Id { get; set; }
        public int UserId { get; set; }
        public int RoleId { get; set; }
    }
}
namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 角色菜单映射信息
    /// </summary>
    public class RoleMenuMapEntity:BaseEntity
    {
        public int Id { get; set; }
        public int RoleId { get; set; }
        public int MenuId { get; set; }
    }
}
namespace Wenyu.Framework.DbModels.Entities
{
    /// <summary>
    /// 日志信息
    /// </summary>
    public class SystemLog
    {
        public int Id { get; set; }
        public DateTime? Date { get; set; }
        public string? Thread { get; set; }
        public string? Level { get; set; }
        public string? Logger { get; set; }
        public string? Message { get; set; }
        public string? Exception { get; set; }
        
    }
}
using Microsoft.EntityFrameworkCore;
using Wenyu.Framework.DbModels.Entities;

namespace Wenyu.Framework.DbModels
{
    public class FrameworkDbContext:DbContext
    {
        private readonly string _connectionString;

        /// <summary>
        /// 带有书库连接字符串的参数
        /// </summary>
        /// <param name="connectionString"></param>
        public FrameworkDbContext(string connectionString) {
            this._connectionString = connectionString;
        }

        public FrameworkDbContext(DbContextOptions<FrameworkDbContext> options)
            :base(options)
        {
            
        }

        public virtual DbSet<UserEntity> UserEntities { get; set; }

        public virtual DbSet<MenuEntity> MenuEntities { get; set; }
        public virtual DbSet<RoleEntity> RoleEntities { get; set; }
        public virtual DbSet<UserRoleMapEntity> UserRoleMapEntities { get; set; }
        public virtual DbSet<RoleMenuMapEntity> RoleMenuMapEntities { get; set; }
        public virtual DbSet<SystemLog> SystemLogs { get; set; }
        
        protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
        {
            if (!optionsBuilder.IsConfigured)
            {
                optionsBuilder.UseSqlServer(this._connectionString);
            }
        }

        /// <summary>
        /// 配置数据库表映射关系
        /// </summary>
        /// <param name="modelBuilder"></param>
        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            modelBuilder.Entity<UserEntity>(entity =>
            {
                entity.ToTable("UserEntity")
                .HasKey(u=>u.UserId);
            });

            modelBuilder.Entity<RoleEntity>(entity =>
            {
                entity.ToTable("RoleEntity")
                .HasKey(u => u.RoleId);
            });
            modelBuilder.Entity<MenuEntity>(entity =>
            {
                entity.ToTable("MenuEntity")
                .HasKey(u => u.Id);
            });
            modelBuilder.Entity<UserRoleMapEntity>(entity =>
            {
                entity.ToTable("UserRoleMapEntity")
                .HasKey(u => u.Id);
            });
            modelBuilder.Entity<RoleMenuMapEntity>(entity =>
            {
                entity.ToTable("RoleMenuMapEntity")
                .HasKey(u => u.Id);
            });


            modelBuilder.Entity<SystemLog>(entity =>
            {
                entity.ToTable("SystemLog")
                .HasKey(u => u.Id);
            });
        }
    }
}

12.4 定义业务逻辑服务类

using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.ModelDto;

namespace Wenyu.Framework.IBusinessServices
{
    public interface IUserService:IBaseService
    {
        public UserEntity Login(LoginReq loginReq);
        
    }
}


using Wenyu.Framework.Common.Helpers;
using Wenyu.Framework.DbModels;
using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.IBusinessServices;
using Wenyu.Framework.ModelDto;

namespace Wenyu.Framework.BusinessServices
{
    public class UserService : BaseService, IUserService
    {
        public UserService(FrameworkDbContext context) : base(context)
        {
        }

        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="loginReq"></param>
        /// <returns></returns>
        public UserEntity? Login(LoginReq loginReq)
        {
            UserEntity? userEntity= this.Query<UserEntity>(x => x.UserName == loginReq.UserName && x.Password == Md5Helper.Encrypt(loginReq.Password)).FirstOrDefault();
            return userEntity;
        }
    }
}

12.5 在控制器类中的使用业务逻辑服务类

using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using Wenyu.Framework.BusinessServices;
using Wenyu.Framework.Common;
using Wenyu.Framework.Common.EnumTypes;
using Wenyu.Framework.Common.Helpers;
using Wenyu.Framework.DbModels.Entities;
using Wenyu.Framework.IBusinessServices;
using Wenyu.Framework.ModelDto;
using Wenyu.Framework.WebCore.SwaggerExtend;
using Wenyu.Framework.WebCore.ToolServices;

namespace Wenyu.Framework.WebApi.Controllers
{
    /// <summary>
    /// 用户相关的api控制器
    /// </summary>
    [ApiController]
    [Route("api/[controller]")]
    [ApiExplorerSettings(IgnoreApi = false,GroupName =nameof(ApiVersions.V1))]
    public class UserController : ControllerBase
    {      
        private readonly ILogger<UserController> _logger;
        private readonly IUserService _userService;
        private readonly JwtToolService _jwtToolService;
        private readonly IMapper _mapper;
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="logger"></param>
        /// <param name="userService"></param>
        /// <param name="mapper"></param>
        public UserController(ILogger<UserController> logger,
            IUserService userService,
            JwtToolService jwtToolService,
            IMapper mapper)
        {
            _logger = logger;
            this._userService = userService;
            this._jwtToolService = jwtToolService;
            this._mapper = mapper;
           
        }

        /// <summary>
        /// 获取用户的分页列表
        /// </summary>
        /// <returns></returns>
        [HttpGet]
        [Route("{pageIndex:int}/{pageSize:int}")]
        [Route("{pageIndex:int}/{pageSize:int}/{searchString}")]
        public async Task<JsonResult> GetUsersPagedAsync(int pageIndex,int pageSize,string? searchString=null)
        {
            PagingData<UserEntity> paging = _userService
                .QueryPage<UserEntity, DateTime>(!string.IsNullOrWhiteSpace(searchString) ? c => c.Name.Contains(searchString) : a=>true,
                pageSize, pageIndex, c => c.CreateTime, false);

            PagingData<UserDto> pagingResult = _mapper.Map<PagingData<UserDto>>(paging);
            JsonResult result= new JsonResult(new ApiDataResult<PagingData<UserDto>>
            {
                Success = true,
                Message = "成功",
                Data = pagingResult
            });
            return await Task.FromResult(result);
        }

        /// <summary>
        /// 新增用户
        /// </summary>
        /// <param name="addUserDto"></param>
        /// <returns></returns>
        [HttpPost]
        public async Task<JsonResult> AddUserAsync([FromBody] AddUserDto addUserDto)
        {
            UserEntity userEntity=_mapper.Map<UserEntity>(addUserDto);
            userEntity.Password = Md5Helper.Encrypt(addUserDto.Password);
            userEntity.Status = addUserDto.IsEnable? StatusEnum.Normal: StatusEnum.Frozen;
            userEntity.UserType = UserTypeEnum.GeneralUser;

            UserEntity user=_userService.Insert(userEntity);
            ApiDataResult<UserEntity>? apiResult = null;
           
            if (user.UserId <= 0)
            {
                string token=_jwtToolService.GetToken( userEntity);
                apiResult = new ApiDataResult<UserEntity>
                {
                    Success = false,
                    Message = "失败",
                    Data = null
                };
            }
            else
            {
                new ApiDataResult<UserEntity>
                {
                    Success = true,
                    Message = "成功",
                    Data = userEntity
                };
            }
            return await Task.FromResult(new JsonResult(apiResult));
        }


        /// <summary>
        /// 登录
        /// </summary>
        /// <param name="loginReq"></param>
        /// <returns></returns>
        [Route("login")]
        [HttpPost]
        public async Task<JsonResult> LoginAsync([FromBody] LoginReq loginReq)
        {
            UserEntity? userEntity = _userService.Login(loginReq);
            ApiDataResult<string>? apiResult = null;
            
            if (userEntity==null)
            {
                apiResult = new ApiDataResult<string>
                {
                    Success = false,
                    Message = "登录失败",
                };
            }
            else
            {
                string token = _jwtToolService.GetToken(userEntity);
                apiResult = new ApiDataResult<string>
                {
                    Success = true,
                    Message = "登录成功",
                    Data = token
                };
            }
            
            return await Task.FromResult(new JsonResult(apiResult));
        }
    }
}

13、JwtToken权限实现

13.1 ToolServices/JwtTokenOption.cs

"JwtTokenOption": {
  "Audiece": "http://localhost:6787",
  "Issuer": "http://localhost:6787",
  "SecurityKey": "gg45454454545454544545454"
}

namespace Wenyu.Framework.WebCore.ToolServices
{
    public class JwtTokenOption
    {
        public string Audiece { get; set; }
        public string Issuer { get; set; }
        public string SecurityKey { get; set; }
    }

}

13.2 ToolServices/JwtToolService.cs

using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Text;
using Wenyu.Framework.DbModels.Entities;

namespace Wenyu.Framework.WebCore.ToolServices
{
    public class JwtToolService
    {
        private readonly JwtTokenOption _jwtOption;

        public JwtToolService(IOptionsMonitor<JwtTokenOption> optionsMonitor)
        {

            _jwtOption = optionsMonitor.CurrentValue;
        }

        public string GetToken(UserEntity user)
        {
            //有效载荷
            var claims = new[]
            {
                new Claim(ClaimTypes.Name,user.Name),
                new Claim(ClaimTypes.Role,"admin"),
                new Claim(ClaimTypes.Role,"teacher"),

                new Claim("UserId",user.UserId.ToString()),
                new Claim("UserName",user.UserName),
                //new Claim("Name",user.Name.ToString()),
                new Claim("UserType",user.UserType.ToString()),
            };

            //加密需要加密key
            SymmetricSecurityKey key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtOption.SecurityKey));

            SigningCredentials creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            JwtSecurityToken token = new JwtSecurityToken(
                issuer: _jwtOption.Issuer,
                audience: _jwtOption.Audiece,
                claims: claims,
                //expires: DateTime.Now.AddMinutes(10),//10分钟过期
                expires: DateTime.Now.AddSeconds(20),//20s过期
                signingCredentials: creds
                );
            string returnToken = new JwtSecurityTokenHandler().WriteToken(token);
            return returnToken;
        }
    }
}

13.3 鉴权

13.3.1 基于JWT鉴权
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.IdentityModel.Tokens;
using System.Text;
using Wenyu.Framework.WebCore.ToolServices;

namespace Wenyu.Framework.WebCore.AuthorizationExtend
{
    public static class AuthorizationExtension
    {
        /// <summary>
        /// 配置鉴权和授权
        /// </summary>
        /// <param name="service"></param>
        public static void AddAuthorizationExt(this WebApplicationBuilder builder)
        {
            JwtTokenOption jwtTokenOption = new JwtTokenOption();
            builder.Configuration.Bind("JwtTokenOption", jwtTokenOption);
            builder.Services
                .AddAuthentication(JwtBearerDefaults.AuthenticationScheme)  //nuget :Microsoft.AspNetCore.Authentication.JwtBearer
                .AddJwtBearer(options =>     //nuget :Microsoft.AspNetCore.Authentication.JwtBearer
                {
                    options.TokenValidationParameters = new TokenValidationParameters()
                    {
                        ValidateIssuer = true, //是否验证Issuer
                        ValidateAudience = true,//是否验证Audience
                        ValidateLifetime = true,//是否验证失效时间
                        ValidateIssuerSigningKey = true,//是否验证SecurityKey

                        ValidIssuer = jwtTokenOption.Issuer,
                        ValidAudience = jwtTokenOption.Audiece,
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtTokenOption.SecurityKey)),
                        ClockSkew = TimeSpan.FromSeconds(0) //token过期后默认300s是有效的,0马上失效
                        //AudienceValidator = (m, n, z) =>
                        //{
                        //    //这里可以写自己定义的验证逻辑
                        //    //return m.
                        //    return true;
                        //},
                        //LifetimeValidator = (notBefore,expires,securityToken,validationParameters) =>
                        //{
                        //    //这里可以写自己定义的验证逻辑
                        //    return true;
                        //}
                    };
                    //options.Events = new JwtBearerEvents
                    //{
                    //    OnAuthenticationFailed = (context) =>
                    //    {
                    //        return Task.CompletedTask;
                    //    },
                    //    OnChallenge = (context) =>
                    //    {
                    //        return Task.CompletedTask;
                    //    },
                    //    OnForbidden = (context) =>
                    //    {
                    //        return Task.CompletedTask;
                    //    },
                    //    OnTokenValidated = (context) =>
                    //    {
                    //        return Task.CompletedTask;
                    //    },
                    //};
                });

            builder.Services.AddAuthorization(config =>
            {
                config.AddPolicy("MenuPolicy", (pollyBuider) =>
                {
                    pollyBuider.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                                .AddRequirements(new MenuAuthorizationRequirement());
                });
            });
            builder.Services.AddTransient<IAuthorizationHandler, MenuAuthorizationHandler>();
        }
        /// <summary>
        /// 使用鉴权和授权
        /// </summary>
        /// <param name="app"></param>
        public static void UseAuthorizationExt(this WebApplication app)
        {
            app.UseAuthentication();//鉴权,将请求携带的信息(token,session,cookies)解析到HttpContext.User中
            app.UseAuthorization();//授权,根据用户信息检查当前用户是否有权请求当前资源
        }
    }
}

Program.cs

#region Authentication和Authorization 鉴权和授权配置
builder.AddAuthorizationExt();
#endregion

app.UseAuthorizationExt();

13.4 基于角色授权

/// <summary>
/// 基于角色的需要鉴权和授权,只要是其中一个角色就能通过
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme,Roles ="admin,teacher")]
public IActionResult GetTest1()
{
    System.Security.Claims.ClaimsPrincipal user = HttpContext.User;
    return new JsonResult(new ApiResult
    {
        Success = true,
        Message = "成功",

    });
}

/// <summary>
/// 基于角色的需要鉴权和授权,必须是所有角色都有才能通过
/// </summary>
/// <returns></returns>
[HttpGet]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme,Roles ="teacher")]
[Authorize(AuthenticationSchemes =JwtBearerDefaults.AuthenticationScheme,Roles ="admin")]
public IActionResult GetTest2()
{
    System.Security.Claims.ClaimsPrincipal user = HttpContext.User;
    return new JsonResult(new ApiResult
    {
        Success = true,
        Message = "成功",

    });
}

13.5 基于策略授权

关键配置

builder.Services.AddAuthorization(config =>
            {
                config.AddPolicy("MenuPolicy", (pollyBuider) =>
                {
                    pollyBuider.AddAuthenticationSchemes(JwtBearerDefaults.AuthenticationScheme)
                                .AddRequirements(new MenuAuthorizationRequirement());
                });
            });
            
builder.Services.AddTransient<IAuthorizationHandler, MenuAuthorizationHandler>();

AuthorizationExtend/MenuAuthorizationHandler

using Microsoft.AspNetCore.Authorization;

namespace Wenyu.Framework.WebCore.AuthorizationExtend
{
    public class MenuAuthorizationHandler : AuthorizationHandler<MenuAuthorizationRequirement>
    {
        protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, MenuAuthorizationRequirement requirement)
        {
            if (context.User.Claims == null||context.User.Claims.Count()<=0)
            {
                context?.Fail();

            }else if (context.User.IsInRole("admin"))
            {
                context?.Succeed(requirement);
            }
            await Task.CompletedTask;
        }
    }
}

AuthorizationExtend/MenuAuthorizationRequirement.cs

using Microsoft.AspNetCore.Authorization;

namespace Wenyu.Framework.WebCore.AuthorizationExtend
{
    public class MenuAuthorizationRequirement : IAuthorizationRequirement
    {
    }
}

14 工具服务类

区别于业务逻辑类,与技术有关,一般放在基础设施层,

不抽离的话也可以放在应用层

14.1 Md5Helper.cs

Helpers/Md5Helper.cs

using System.Security.Cryptography;
using System.Text;


namespace Wenyu.Framework.Common.Helpers
{
    public class Md5Helper
    {
        public static string Encrypt(string str)
        {

            string pwd = String.Empty;

            MD5 md5 = MD5.Create();

            // 编码UTF8/Unicode 
            byte[] s = md5.ComputeHash(Encoding.UTF8.GetBytes(str));

            // 转换成字符串
            for (int i = 0; i < s.Length; i++)
            {
                //格式后的字符是小写的字母
                //如果使用大写(X)则格式后的字符是大写字符
                pwd = pwd + s[i].ToString("X");

            }

            return pwd;
        }
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值