NewGaoKaoApi/.docs/contents/guide/cheat-sheet.md

20 KiB
Raw Blame History

Z 主要知识点

AOP

本项目多处采用面向切面编程思想——AOP除了广义上的过滤器和中间件以外主要通过动态代理的形式来实现AOP编程思想主要的案例共有四个分别是
1、服务日志AOP
2、服务InMemory缓存AOP
3、服务Redis缓存AOP
4、服务事务AOP

具体的代码可以在 New_College\New_College\AOP 文件夹下查看。

与此同时多个AOP也设置了阀门来控制是否开启具体的可以查看 appsettings.json 中的:

  "AppSettings": {
    "RedisCachingAOP": {
      "Enabled": false,
      "ConnectionString": "127.0.0.1:6319"
    },
    "MemoryCachingAOP": {
      "Enabled": true
    },
    "LogAOP": {
      "Enabled": false
    },
    "TranAOP": {
      "Enabled": false
    },
    "SqlAOP": {
      "Enabled": false
    }
  },

Appsettings

整个系统通过一个封装的操作类 Appsettings.cs 来控制配置文件 appsettings.json 文件,
操作类地址在:\New_College.Common\Helper 文件夹下。
具体的使用方法是:

Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" })

// 里边的参数,按照 appsettings.json 中设置的层级顺序来写,可以获取到指定的任意内容。

AspNetCoreRateLimit

系统使用 AspNetCoreRateLimit 组件来实现ip限流 1、添加 nuget 包:

<PackageReference Include="AspNetCoreRateLimit" Version="3.0.5" />

2、注入服务 IpPolicyRateLimitSetup.cs

services.AddIpPolicyRateLimitSetup(Configuration);

3、配置中间件

 // Ip限流,尽量放管道外层
 app.UseIpRateLimiting();

4、配置数据

具体的内容,自行百度即可

  "IpRateLimiting": {
    "EnableEndpointRateLimiting": true,
    "StackBlockedRequests": false,
    "RealIpHeader": "X-Real-IP",
    "ClientIdHeader": "X-ClientId",
    "HttpStatusCode": 429,//返回状态码
    "GeneralRules": [//规则,结尾一定要带*
      {
        "Endpoint": "*",
        "Period": "1m",
        "Limit": 120
      },
      {
        "Endpoint": "*:/api/blog*",
        "Period": "1m",
        "Limit": 30
      }
    ]

  }

Async-Await

整个系统采用 async/await 异步编程,符合主流的开发模式,
特别是对多线程开发很友好。

Authorization-Ids4

本系统 v2.0 版本(目前的系统已经集成 ids4jwt,并且可以自由切换),已经支持了统一授权认证,和 blog 项目、Admin 项目、DDD 项目等一起,使用一个统一的认证中心。

具体的代码参考:.\New_College\Extensions 文件夹下的 Authorization_Ids4Setup.cs ,注意需要引用指定的 nuget 包,核心代码如下:

    //【认证】
  services.AddAuthentication(o =>
  {
      o.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
      o.DefaultChallengeScheme = nameof(ApiResponseHandler);
      o.DefaultForbidScheme = nameof(ApiResponseHandler);
  })
  // 2.添加Identityserver4认证
  .AddIdentityServerAuthentication(options =>
  {
      options.Authority = Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" });
      options.RequireHttpsMetadata = false;
      options.ApiName = Appsettings.app(new string[] { "Startup", "IdentityServer4", "ApiName" });
      options.SupportedTokens = IdentityServer4.AccessTokenValidation.SupportedTokens.Jwt;
      options.ApiSecret = "api_secret";

  })


如何在Swagger中配置Ids4

很简单,直接在 SwaggerSetup.cs 中直接接入 oauth、Implicit 即可:

 //接入identityserver4
 c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
 {
     Type = SecuritySchemeType.OAuth2,
     Flows = new OpenApiOAuthFlows
     {
         Implicit = new OpenApiOAuthFlow
         {
             AuthorizationUrl = new Uri($"{Appsettings.app(new string[] { "Startup", "IdentityServer4", "AuthorizationUrl" })}/connect/authorize"),
             Scopes = new Dictionary<string, string> {
             {
                 "new_college.api","ApiResource id"
             }
         }
         }
     }
 });

然后在 IdentityServer4 项目中,做指定的修改,配置 8081 的回调地址:

 new Client {
     ClientId = "blogadminjs",
     ClientName = "Blog.Admin JavaScript Client",
     AllowedGrantTypes = GrantTypes.Implicit,
     AllowAccessTokensViaBrowser = true,

     RedirectUris =
     {
         "http://vueadmin.neters.club/callback",
         // 这里要配置回调地址
         "http://localhost:8081/oauth2-redirect.html" 
     },
     PostLogoutRedirectUris = { "http://vueadmin.neters.club" },
     AllowedCorsOrigins =     { "http://vueadmin.neters.club" },

     AllowedScopes = {
         IdentityServerConstants.StandardScopes.OpenId,
         IdentityServerConstants.StandardScopes.Profile,
         "roles",
         "new_college.api"
     }
 },

然后再 Swagger 中,配置登录授权:

swagger

Authorization-JWT

如果你不想使用 IdentityServer4 的话,也可以使用 JWT 认证,同样是是New_College\New_College\Extensions 文件夹下的 AuthorizationSetup.cs 中有关认证的部分:

 1.添加JwtBearer认证服务
.AddJwtBearer(o =>
{
    o.TokenValidationParameters = tokenValidationParameters;
    o.Events = new JwtBearerEvents
    {
        OnAuthenticationFailed = context =>
        {
            // 如果过期,则把<是否过期>添加到,返回头信息中
            if (context.Exception.GetType() == typeof(SecurityTokenExpiredException))
            {
                context.Response.Headers.Add("Token-Expired", "true");
            }
            return Task.CompletedTask;
        }
    };
})

AutoMapper

使用 AutoMapper 组件来实现 Dto 模型的传输转换,具体的用法,可以查看:
New_College\New_College\Extensions 文件夹下的 AutoMapperSetup.cs 扩展类,
通过引用 AutoMapperAutoMapper.Extensions.Microsoft.DependencyInjection 两个 nuget 包,并设置指定的 profile 文件,来实现模型转换控制。

// 比如如何定义:
 public class CustomProfile : Profile
 {
     /// <summary>
     /// 配置构造函数,用来创建关系映射
     /// </summary>
     public CustomProfile()
     {
         CreateMap<BlogArticle, BlogViewModels>();
         CreateMap<BlogViewModels, BlogArticle>();
     }
 }


// 比如如何使用
models = _mapper.Map<BlogViewModels>(blogArticle);

具体的查看项目中代码即可。

CORS

在线项目使用的是 nginx 跨域代理,但是同时也是支持 CORS 代理:
1、注入服务 services.AddCorsSetup(); 具体代码 New_College\New_College\Extensions 文件夹下的 CorsSetup.cs 扩展类;
2、配置中间件 app.UseCors("LimitRequests"); ,要注意中间件顺序;
3、配置自己项目的前端端口通过在 appsettings.json 文件中配置自己的前端项目 ip:端口 ,来实现跨域:

  "Startup": {
    "Cors": {
      "IPs": "http://127.0.0.1:2364,http://localhost:2364,http://localhost:8080,http://localhost:8021,http://localhost:1818"
    }
  },

DI-AutoFac

项目使用了依赖注入,除了原生的依赖注入以外,更多的使用的是第三方组件 Autofac
1、引用依赖包

    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="5.0.1" />
    <PackageReference Include="Autofac.Extras.DynamicProxy" Version="4.5.0" />

主要是第一个 nuget 包,下边的是为了实现动态代理 AOP 操作;

2、项目之间采用引用解耦的方式通过反射来注入服务层和仓储层的程序集 dll 来实现批量注入,更方便,以后每次新增和修改 Service 层和 Repository 层,只需要 F6 编译一下即可,具体代码查看 Startup.cs



        // 注意在CreateDefaultBuilder中添加Autofac服务工厂
        public void ConfigureContainer(ContainerBuilder builder)
        {
            var basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;
            //builder.RegisterType<AdvertisementServices>().As<IAdvertisementServices>();


            #region 带有接口层的服务注入


            var servicesDllFile = Path.Combine(basePath, "New_College.Services.dll");
            var repositoryDllFile = Path.Combine(basePath, "New_College.Repository.dll");

            if (!(File.Exists(servicesDllFile) && File.Exists(repositoryDllFile)))
            {
                throw new Exception("Repository.dll和service.dll 丢失因为项目解耦了所以需要先F6编译再F5运行请检查 bin 文件夹,并拷贝。");
            }



            // AOP 开关,如果想要打开指定的功能,只需要在 appsettigns.json 对应对应 true 就行。
            var cacheType = new List<Type>();
            if (Appsettings.app(new string[] { "AppSettings", "RedisCachingAOP", "Enabled" }).ObjToBool())
            {
                builder.RegisterType<BlogRedisCacheAOP>();
                cacheType.Add(typeof(BlogRedisCacheAOP));
            }
            if (Appsettings.app(new string[] { "AppSettings", "MemoryCachingAOP", "Enabled" }).ObjToBool())
            {
                builder.RegisterType<BlogCacheAOP>();
                cacheType.Add(typeof(BlogCacheAOP));
            }
            if (Appsettings.app(new string[] { "AppSettings", "TranAOP", "Enabled" }).ObjToBool())
            {
                builder.RegisterType<BlogTranAOP>();
                cacheType.Add(typeof(BlogTranAOP));
            }
            if (Appsettings.app(new string[] { "AppSettings", "LogAOP", "Enabled" }).ObjToBool())
            {
                builder.RegisterType<BlogLogAOP>();
                cacheType.Add(typeof(BlogLogAOP));
            }

            // 获取 Service.dll 程序集服务,并注册
            var assemblysServices = Assembly.LoadFrom(servicesDllFile);
            builder.RegisterAssemblyTypes(assemblysServices)
                      .AsImplementedInterfaces()
                      .InstancePerDependency()
                      .EnableInterfaceInterceptors()//引用Autofac.Extras.DynamicProxy;
                      .InterceptedBy(cacheType.ToArray());//允许将拦截器服务的列表分配给注册。

            // 获取 Repository.dll 程序集服务,并注册
            var assemblysRepository = Assembly.LoadFrom(repositoryDllFile);
            builder.RegisterAssemblyTypes(assemblysRepository)
                   .AsImplementedInterfaces()
                   .InstancePerDependency();

            #endregion

            #region 没有接口层的服务层注入

            //因为没有接口层,所以不能实现解耦,只能用 Load 方法。
            //注意如果使用没有接口的服务,并想对其使用 AOP 拦截,就必须设置为虚方法
            //var assemblysServicesNoInterfaces = Assembly.Load("New_College.Services");
            //builder.RegisterAssemblyTypes(assemblysServicesNoInterfaces);

            #endregion

            #region 没有接口的单独类 class 注入

            //只能注入该类中的虚方法
            builder.RegisterAssemblyTypes(Assembly.GetAssembly(typeof(Love)))
                .EnableClassInterceptors()
                .InterceptedBy(cacheType.ToArray());

            #endregion


            // 这里和注入没关系,只是获取注册列表,请忽略
            tsDIAutofac.AddRange(assemblysServices.GetTypes().ToList());
            tsDIAutofac.AddRange(assemblysRepository.GetTypes().ToList());
        }

3、然后 Program.cs 中也要加一句话:.UseServiceProviderFactory(new AutofacServiceProviderFactory()) //<--NOTE THIS

DI-NetCore

除了主要的 Autofac 依赖注入以外,也减少的使用了原生的依赖注入方式,很简单,比如这样的:


            services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
            // 注入权限处理器
            services.AddScoped<IAuthorizationHandler, PermissionHandler>();
            services.AddSingleton(permissionRequirement);

Filter

项目中一共有四个过滤器

1、GlobalAuthorizeFilter.cs —— 全局授权配置,添加后,就可以不用在每一个控制器上添加 [Authorize] 特性但是3.1版本好像有些问题,【暂时放弃使用】;
2、GlobalExceptionFilter.cs —— 全局异常处理,实现 actionContext 级别的异常日志收集;
3、GlobalRoutePrefixFilter.cs —— 全局路由前缀公约,统计在路由上加上前缀;
4、UseServiceDIAttribute.cs —— 测试注入,【暂时无用】;

文件地址在 .\New_College\Filter 文件夹下,其中核心的是 2 个,重点使用的是 1 个 —— 全局异常错误日志 GlobalExceptionsFilter: 通过注册在 MVC 服务 services.AddControllers() 中,实现全局异常过滤:

 services.AddControllers(o =>
 {
     // 全局异常过滤
     o.Filters.Add(typeof(GlobalExceptionsFilter));
     // 全局路由权限公约
     //o.Conventions.Insert(0, new GlobalRouteAuthorizeConvention());
     // 全局路由前缀,统一修改路由
     o.Conventions.Insert(0, new GlobalRoutePrefixFilter(new RouteAttribute(RoutePrefix.Name)));
 })

Framework

项目采用 服务+仓储+接口 的多层结构,使用依赖注入,并且通过解耦项目,较完整的实现了 DIP 原则:
高层模块不应该依赖于底层模块,二者都应该依赖于抽象。
抽象不应该依赖于细节,细节应该依赖于抽象。

同时项目也封装了:
CodeFirst 初始化数据库以及数据;
DbFirst 根据数据库(支持多库),生成多层代码,算是简单代码生成器;
其他功能,核心功能与进度

Log

通过集成 Log4Net 组件,完美配合 NetCore 官方的 ILogger<T> 接口,实现对日志的管控,引用 nugetMicrosoft.Extensions.Logging.Log4Net.AspNetCore: Program.cs

  webBuilder
  .UseStartup<Startup>()
  .ConfigureLogging((hostingContext, builder) =>
  {
      //该方法需要引入Microsoft.Extensions.Logging名称空间
      builder.AddFilter("System", LogLevel.Error); //过滤掉系统默认的一些日志
      builder.AddFilter("Microsoft", LogLevel.Error);//过滤掉系统默认的一些日志

      //添加Log4Net
      //var path = Directory.GetCurrentDirectory() + "\\log4net.config"; 
      //不带参数表示log4net.config的配置文件就在应用程序根目录下也可以指定配置文件的路径
      //需要添加nuget包Microsoft.Extensions.Logging.Log4Net.AspNetCore
      builder.AddLog4Net();
  });

然后直接在需要的地方注入使用,比如在控制器中 public UserController(ILogger<UserController> logger)

然后就可以使用了。

注意:日志 其实是分为两部分的:
netcore输出(控制台、输出窗口等) 和 ILogger 持久化
两者对应配置也不一样,就比如上边的过滤,是针对日志持久化的,如果想要对控制台进行控制,需要配置 appsettings.json 中的 Logging 节点

MemoryCache

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

Middleware

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

MiniProfiler

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

publish

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

Redis

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

Repository

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

SeedData

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

SignalR

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

SqlSugar

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

SqlSugar-Codefirst&DataSeed

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

SqlSugar-SqlAOP

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

Swagger

精力有限,还是更新中...
如果你愿意帮忙可以直接在GitHub中提交pull request
我会在后边的贡献者页面里,列出你的名字和项目地址做推广

T4

项目集成 T4 模板 .\New_College.FrameWork 层,目的是可以一键生成项目模板代码。
1、需要在 DbHelper.ttinclude 中配置连接数据库连接字符串;
2、针对每一层的代码就去指定的 .tt 模板,直接 CTRL+S 保存即可;

注意,目前的代码是 SqlServer 版本的,其他数据库版本的,可以去群文件查看。

Test-xUnit

项目简单使用了单元测试,通过 xUnit 组件,具体的可以查看 New_College.Tests 层相关代码。
目前单元测试用例还比较少,大家可以自行添加。

Temple-Nuget

本项目封装了 Nuget 自定义模板,你可以根据这个模板,一键创建自己的项目名,具体的操作,可以双击项目根目录下的 CreateYourProject.bat ,可以参考 #如何项目重命名

同时,你也可以再 Nuget 管理器中,搜索到: nuget

UserInfo

项目中封装了获取用户信息的代码:
.\New_College.Common\HttpContextUser 文件夹下 AspNetUser.cs 实现类和 IUser.cs 接口。

如果使用,首先需要注册相应的服务,参见:.\New_College\Extensions 文件夹下的 HttpContextSetup.cs
然后,就直接在控制器构造函数中,注入接口 IUser 即可;

注意
1、如果要想获取指定的服务必须登录也就是必须要在 Header 中传递有效 Token ,这是肯定的。
2、如果要获取用户信息一定要在中间件 app.UseAuthentication() 之后(不要问为什么),控制器肯定在它之后,所以能获取到;
3、【并不是】一定需要添加 [Authorize] 特性,如果你加了这个特性,可以直接获取,但是如果不加,可以从我的 AspNetUser.cs 方法中,有一个直接从 Header 中解析的方法 List<string> GetUserInfoFromToken(string ClaimType);

 public string GetToken()
 {
     return _accessor.HttpContext.Request.Headers["Authorization"].ObjToString().Replace("Bearer ", "");
 }

 public List<string> GetUserInfoFromToken(string ClaimType)
 {

     var jwtHandler = new JwtSecurityTokenHandler();
     if (!string.IsNullOrEmpty(GetToken()))
     {
         JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(GetToken());

         return (from item in jwtToken.Claims
                 where item.Type == ClaimType
                 select item.Value).ToList();
     }
     else
     {
         return new List<string>() { };
     }
 }