【笔记】ASP.NET Core 2.2 Web API —— 学习笔记

当年刚接触 ASP.NET Core 2.2 时,留下的学习笔记。现在把它挪到 CSDN,也是对过去学习 ASP.NET Core 痕迹进行记录。

VS 2019 + ASP.NET Core 2.2 + sqlSugarCore (ORM)

1. 仓储模式 + 服务 + 抽象接口

1.1 新建asp.net core 2.2 WebApi项目

nmmking.Core.Api
nmmking.Core.Common
nmmking.Core.IRepository
nmmking.Core.IServices
nmmking.Core.Model
nmmking.Core.Repository
nmmking.Core.Services
nmmking.Core.Test

2.nmmking.Core.Model 使用 Sqlsuger Code first

2.1用 nuget 安装 sqlSugarCore ,新建 model 类

namespace nmmking.Core.Model.Models
{public class User{[SugarColumn(IsNullable =false,IsPrimaryKey =true,IsIdentity =true)]public int Id { get; set; }[SugarColumn(Length =50)]public string Name { get; set; }[SugarColumn(Length =50,IsNullable =true)]public string Password { get; set; }}
}

2.2 新建IDbContext接口

namespace nmmking.Core.Model
{public interface IDbContext{/// <summary>/// 操作数据库对象/// </summary>SqlSugarClient db { get; }/// <summary>/// 创建数据表/// </summary>/// <param name="Backup">是否备份</param>/// <param name="StringDefaultLength">string类型映射的长度</param>/// <param name="types">要创建的数据表</param>void CreateTable(bool Backup = false, int StringDefaultLength = 50, params Type[] types);/// <summary>/// 创建表/// </summary>/// <param name="Backup">是否备份</param>/// <param name="StringDefaultLength">string类型映射的长度</param>//public void CreateAllTable(bool Backup = false, int StringDefaultLength = 50);}
}

2.3 新建MyContext类

public class MyContext: IDbContext{public SqlSugarClient db{get{return new SqlSugarClient(new ConnectionConfig(){ConnectionString = "server=.;database=nmmkingCoreDb;uid=sa;pwd=123456;",DbType = DbType.SqlServer,//数据库类型IsAutoCloseConnection = true,//自动释放数据务,如果存在事务,在事务结束后释放InitKeyType = InitKeyType.Attribute,//从实体特性中读取主键自增列信息});}}//public static SqlSugarClient GetInstance()//{//    db = new SqlSugarClient(new ConnectionConfig()//    {//        ConnectionString = "server=.;database=StudentDb;uid=sa;pwd=123456;",//        DbType = DbType.SqlServer,//设置数据库类型//        IsAutoCloseConnection = true,//自动释放数据库,如果存在事务,在事务结束之后释放。//        InitKeyType = InitKeyType.Attribute//从实体特性中读取主键自增列信息   //    });//    //db.Aop.OnLogExecuting = (sql, pars) =>//    //{//    //    Console.WriteLine(sql + "\r\n" + db.Utilities.SerializeObject//    //        (pars.ToDictionary(it => it.ParameterName, it => it.Value)));//    //    Console.WriteLine();//    //};//    return db;//}public void CreateTable(bool Backup = false, int StringDefaultLength = 50, params Type[] types){//设置varchar的默认长度db.CodeFirst.SetStringDefaultLength(StringDefaultLength);db.DbMaintenance.CreateDatabase();//创建表if (Backup){db.CodeFirst.BackupTable().InitTables(types);}else{db.CodeFirst.InitTables(types);}}public SimpleClient<User> userDb { get { return new SimpleClient<User>(db); } }//public SqlSugarClient db => throw new NotImplementedException();}

2.4 使用

namespace nmmking.Core.Test
{class Program{static void Main(string[] args){MyContext context = new MyContext();context.CreateTable(false, 50, typeof(User));//AppDbContext context = new AppDbContext();//context.CreateTable(false, 50, typeof(Students), typeof(Schools));Console.WriteLine("Hello World!");}}
}

3. 仓储层

3.1 IBaseRepository

namespace nmmking.Core.IRepository
{public interface IBaseRepository<T> where T:class{Task<T> QueryById(object id, bool IsUseCache = false); //根据id查询Task<List<T>> GetEntities(Expression<Func<T, bool>> whereLambda); //根据查询条件查询Task<bool> Add(T entity);Task<bool> DeleteById(object Id);Task<bool> Update(T entity);}
}

3.2 BaseRepository

namespace nmmking.Core.Repository
{public class BaseRepository<T> : IBaseRepository<T> where T : class, new(){private readonly IDbContext _dbContext;public BaseRepository(IDbContext dbContext){this._dbContext = dbContext;}// 增加单条数据public async Task<bool> Add(T entity){return await _dbContext.db.Insertable<T>(entity).ExecuteCommandAsync() > 0;}// 根据id删除单条数据public async Task<bool> DeleteById(object id){return await _dbContext.db.Deleteable<T>(id).ExecuteCommandHasChangeAsync();}// 查询数据列表public async Task<List<T>> GetEntities(Expression<Func<T, bool>> whereExpression){return await _dbContext.db.Queryable<T>().WhereIF(whereExpression != null, whereExpression).ToListAsync();}// 根据Id查询一条数据public async Task<T> QueryById(object id, bool IsUseCache =false){return await _dbContext.db.Queryable<T>().WithCacheIF(IsUseCache).In(id).SingleAsync();}// 更新实体数据public Task<bool> Update(T entity){var i = await Task.Run(() => dbcontext.Updateable(model).ExecuteCommand());return i > 0;}}
}

4.读取 appsettings.json 文件

4.1 nuget 安装 Microsoft.Extensions.Configuration.Binder,Microsoft.Extensions.Configuration.Json。

4.2 新建 Appsettings 类

namespace nmmking.Core.Common.Helper
{/// <summary>/// appsettings.json操作类/// </summary>public class Appsettings{static IConfiguration Configuration { get; set; }static string contentPath { get; set; }static Appsettings(){//如果你把配置文件 是 根据环境变量来分开了,可以这样写//Path = $"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json";Configuration = new ConfigurationBuilder().Add(new JsonConfigurationSource { Path = "appsettings.json", ReloadOnChange = true })//请注意要把当前appsetting.json 文件->右键->属性->复制到输出目录->始终复制.Build();}/// <summary>/// 封装要操作的字符/// </summary>/// <param name="sections">节点配置</param>/// <returns></returns>public static string app(params string[] sections){try{if (sections.Any()){return Configuration[string.Join(":", sections)];}}catch (Exception) { }return "";}/// <summary>/// 递归获取配置信息数组/// </summary>/// <typeparam name="T"></typeparam>/// <param name="sections"></param>/// <returns></returns>public static List<T> app<T>(params string[] sections){List<T> list = new List<T>();// 引用 Microsoft.Extensions.Configuration.Binder 包Configuration.Bind(string.Join(":", sections), list);return list;}}
}

4.3 使用

string iss = Appsettings.app(new string[] { "Audience", "Issuer" });
string aud = Appsettings.app(new string[] { "Audience", "Audience" }); 

5.使用Cors跨域

5.1 允许所有域名访问接口
在Startup.cs文件的ConfigureServices方法添加:

public void ConfigureServices(IServiceCollection services){services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);services.AddCors(c => {c.AddPolicy("LimitRequests", policy => {policy.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();});});services.AddTransient(typeof(IBaseRepository<>), typeof(BaseRepository<>));services.AddTransient<IUserService, UserService>();services.AddTransient<IDbContext, MyContext>();}
在Startup.cs文件的 Configure 方法 添加:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseCors("LimitRequests");app.UseMvc();
}

运行:http://localhost:61576/api/values

测试一下,打开百度地址:https://www.baidu.com/,按F12,在Console输入:

$.get("http://localhost:61576/api/values",function(data,status){console.log(data);
});

5.2 限制域的访问

            services.AddCors(c => {c.AddPolicy("LimitRequests", policy => {policy//.AllowAnyOrigin().WithOrigins(new string[]{ "http://localhost:8881" })//.WithOrigins(Appsettings.app(new string[] { "Startup", "Cors", "IPs" }).Split(',')).AllowAnyHeader().AllowAnyMethod();});});
public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseCors("LimitRequests");app.UseMvc();}

5.3 跨域访问

下载 nginx ,http://nginx.org/en/download.html,配置 nginx。

新建 index.html

<html>
<head>
<script src="https://s3.pstatp.com/cdn/expire-1-M/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
$(document).ready(function(){$.get("http://localhost:61576/api/values",function(data,status){alert(data);console.log(data);});
});
</script>
</head><body>
<p>If you click on me, I will disappear.</p>
</body></html> 

出现跨域问题

修改访问地址

            services.AddCors(c => {c.AddPolicy("LimitRequests", policy => {policy//.AllowAnyOrigin().WithOrigins(new string[]{ "http://localhost:8889" })//.WithOrigins(Appsettings.app(new string[] { "Startup", "Cors", "IPs" }).Split(',')).AllowAnyHeader().AllowAnyMethod();});});
刷新一下index.html,弹出界面,访问成功

6.使用Log4Net日志处理

安装

注册服务

public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>WebHost.CreateDefaultBuilder(args).UseStartup<Startup>().ConfigureLogging((hostingContext, builder) =>{//过滤掉系统默认的一些日志builder.AddFilter("System", LogLevel.Error);builder.AddFilter("Microsoft", LogLevel.Error);//builder.AddFilter();//可配置文件var path = Path.Combine(Directory.GetCurrentDirectory(),"Log4net.config");builder.AddLog4Net(path);});

配置文件,下面文件配置了error和info两种级别的日志

<?xml version="1.0" encoding="utf-8"?>
<log4net><!-- 将日志以回滚文件的形式写到文件中 --><!-- 按日期切分日志文件,并将日期作为日志文件的名字 --><!--Error--><appender name="ErrorLog" type="log4net.Appender.RollingFileAppender"><file value="Log/"/><appendToFile value="true" /><rollingStyle value="Date" /><!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置--><datePattern value="&quot;GlobalExceptionLogs_&quot;yyyyMMdd&quot;.log&quot;" /><!--日志文件名是否为静态--><StaticLogFileName value="false"/><!--多线程时采用最小锁定--><lockingModel type="log4net.Appender.FileAppender+MinimalLock" /><!--布局(向用户显示最后经过格式化的输出信息)--><layout type="log4net.Layout.PatternLayout"><conversionPattern value="%date| %-5level %newline%message%newline--------------------------------%newline" /></layout><filter type="log4net.Filter.LevelRangeFilter"><levelMin value="ERROR" /><levelMax value="FATAL" /></filter></appender><!--Error--><!--Info--><appender name="InfoLog" type="log4net.Appender.RollingFileAppender"><!--定义文件存放位置--><file value="Log/"/><appendToFile value="true" /><rollingStyle value="Date" /><!--日志文件名是否为静态--><StaticLogFileName value="false"/><!--日期的格式,每天换一个文件记录,如不设置则永远只记录一天的日志,需设置--><datePattern value="&quot;GlobalInfoLogs_&quot;yyyyMMdd&quot;.log&quot;" /><!--多线程时采用最小锁定--><lockingModel type="log4net.Appender.FileAppender+MinimalLock" /><!--布局(向用户显示最后经过格式化的输出信息)--><layout type="log4net.Layout.PatternLayout"><conversionPattern value="%date| %-5level %newline%message%newline--------------------------------%newline" /></layout><filter type="log4net.Filter.LevelRangeFilter"><levelMin value="DEBUG" /><levelMax value="WARN" /></filter></appender><!--Info--><root><!-- 控制级别,由低到高:ALL|DEBUG|INFO|WARN|ERROR|FATAL|OFF --><!-- 比如定义级别为INFO,则INFO级别向下的级别,比如DEBUG日志将不会被记录 --><!-- 如果没有定义LEVEL的值,则缺省为DEBUG --><level value="ALL" /><!-- 按日期切分日志文件,并将日期作为日志文件的名字 --><appender-ref ref="ErrorLog" /><appender-ref ref="InfoLog" /></root>
</log4net>

使用

[Produces("application/json")][Route("api/Values")][AllowAnonymous]public class ValuesController : ControllerBase{private readonly IUserService _userService;private readonly ILogger<ValuesController> _logger;public ValuesController(IUserService userService,ILogger<ValuesController> logger){this._userService = userService;this._logger = logger;}// GET api/values[HttpGet]public ActionResult<IEnumerable<string>> Get(){_logger.LogError("this is an error");return new string[] { "value1", "value2" };}
}

7.JWT基于策略的授权

安装 Microsoft.IdentityModel.Tokens 和 System.IdentityModel.Tokens.Jwt

新建Jwt操作类

 public class JwtHelper{/// <summary>/// 颁发JWT字符串/// </summary>/// <param name="tokenModel"></param>/// <returns></returns>public static string IssueJWT(TokenModelJWT tokenModel){var dateTime = DateTime.UtcNow;string iss = Appsettings.app(new string[] { "Audience", "Issuer" });string aud = Appsettings.app(new string[] { "Audience", "Audience" });string secret = Appsettings.app(new string[] { "Audience", "Secret" });//var claims = new Claim[] //oldvar claims = new List<Claim>{//下边为Claim的默认配置new Claim(JwtRegisteredClaimNames.Jti, tokenModel.Uid.ToString()),new Claim(JwtRegisteredClaimNames.Iat, $"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}"),new Claim(JwtRegisteredClaimNames.Nbf,$"{new DateTimeOffset(DateTime.Now).ToUnixTimeSeconds()}") ,//这个就是过期时间,目前是过期100秒,可自定义,注意JWT有自己的缓冲过期时间new Claim (JwtRegisteredClaimNames.Exp,$"{new DateTimeOffset(DateTime.Now.AddSeconds(100)).ToUnixTimeSeconds()}"),new Claim(JwtRegisteredClaimNames.Iss,iss),new Claim(JwtRegisteredClaimNames.Aud,aud),//new Claim(ClaimTypes.Role,tokenModel.Role),//为了解决一个用户多个角色(比如:Admin,System),用下边的方法};// 可以将一个用户的多个角色全部赋予;// 作者:DX 提供技术支持;claims.AddRange(tokenModel.Role.Split(',').Select(s => new Claim(ClaimTypes.Role, s)));//秘钥 (SymmetricSecurityKey 对安全性的要求,密钥的长度太短会报出异常)var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(secret));var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);var jwt = new JwtSecurityToken(issuer: iss,claims: claims,signingCredentials: creds);var jwtHandler = new JwtSecurityTokenHandler();var encodedJwt = jwtHandler.WriteToken(jwt);return encodedJwt;}/// <summary>/// 解析/// </summary>/// <param name="jwtStr"></param>/// <returns></returns>public static TokenModelJWT SerializeJWT(string jwtStr){var jwtHandler = new JwtSecurityTokenHandler();JwtSecurityToken jwtToken = jwtHandler.ReadJwtToken(jwtStr);object role = new object(); ;try{jwtToken.Payload.TryGetValue(ClaimTypes.Role, out role);}catch (Exception e){Console.WriteLine(e);throw;}var tm = new TokenModelJWT{Uid = Convert.ToInt32(jwtToken.Id),Role = role != null ? role.ToString() : "",};return tm;}}/// <summary>/// 令牌/// </summary>public class TokenModelJWT{/// <summary>/// Id/// </summary>public long Uid { get; set; }/// <summary>/// 角色/// </summary>public string Role { get; set; }/// <summary>/// 职能/// </summary>public string Work { get; set; }}

配置文件appsettings.json

{"Logging": {"LogLevel": {"Default": "Warning"}},"Audience": {"Secret": "sdfsdfsrty45634kkhllghtdgdfss345t678fs", //至少要16位"Issuer": "nmmking.Core","Audience": "yika"},"Startup": {"Cors": {"IPs": "http://127.0.0.1:2364,http://localhost:2364,http://localhost:8080,http://localhost:8021,http://localhost:1818"}},"AllowedHosts": "*"}
在Startup.cs文件注册和配置到管道

            #region 基于策略的授权(简单版)// 1【授权】、这个和上边的异曲同工,好处就是不用在controller中,写多个 roles 。// 然后这么写 [Authorize(Policy = "Admin")]services.AddAuthorization(options =>{options.AddPolicy("Client", policy => policy.RequireRole("Client").Build());options.AddPolicy("Admin", policy => policy.RequireRole("Admin").Build());options.AddPolicy("SystemOrAdmin", policy => policy.RequireRole("Admin", "System"));});#endregion#region 【认证】//读取配置文件var audienceConfig = Configuration.GetSection("Audience");var symmetricKeyAsBase64 = audienceConfig["Secret"];var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);var signingKey = new SymmetricSecurityKey(keyByteArray);//2.1【认证】services.AddAuthentication(x =>{x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;}).AddJwtBearer(o =>{o.TokenValidationParameters = new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = signingKey,ValidateIssuer = true,ValidIssuer = audienceConfig["Issuer"],//发行人ValidateAudience = true,ValidAudience = audienceConfig["Audience"],//订阅人ValidateLifetime = true,ClockSkew = TimeSpan.Zero,RequireExpirationTime = true,};});#endregion
app.UseAuthentication();

接口 GetJWTToken

    [Produces("application/json")][Route("api/[controller]/[action]")]public class ValuesController : ControllerBase{}
  /// <summary>/// 登录接口:随便输入字符,获取token,然后添加 Authoritarian/// </summary>/// <param name="name"></param>/// <param name="pass"></param>/// <returns></returns>[HttpGet]public async Task<object> GetJWTToken(string name, string pass){string jwtStr = string.Empty;bool suc = false;//这里就是用户登陆以后,通过数据库去调取数据,分配权限的操作//这里直接写死了if (string.IsNullOrEmpty(name) || string.IsNullOrEmpty(pass)){return new JsonResult(new{Status = false,message = "用户名或密码不能为空"});}TokenModelJWT tokenModel = new TokenModelJWT();tokenModel.Uid = 1;tokenModel.Role = name;jwtStr = JwtHelper.IssueJWT(tokenModel);suc = true;return Ok(new{success = suc,token = jwtStr});}

测试1:测试 Get 

        /// <summary>/// 这个也需要认证,只不过登录即可,不一定是Admin/// </summary>/// <param name="id"></param>/// <returns></returns>[HttpGet][Authorize]public ActionResult<string> Get(){return "value";}
token 逾期,访问 Get 接口, 结果提示没有权限(未授权),如下图所示:

用户重新登录获取 token (这里没有指定的用户,只要登录就行),如下图所示:

 ​​​​​​再次访问 Get 接口,访问成功如下图所示:

接口2:

        /// <summary>/// 这个需要认证,角色必须是Admin,其他的不需要/// </summary>/// <returns></returns>[HttpGet][Authorize(Policy = "SystemOrAdmin")]public ActionResult<IEnumerable<string>> GetData(){_logger.LogError("this is an error");return new string[] { "value1", "value2" };}

随意的用户登录获取 token,如下所示

 登录的用户不是 Admin,结果是拒绝访问

 如果是admin用户,请求参数和请求头如下:

请求成功 

 

8.Swagger和JWT设置

nuget 安装 Microsoft.OpenApi ,Swashbuckle.AspNetCore,Swashbuckle.AspNetCore.Filters。

在Startup.cs设置:

private const string ApiName = "nmmking.Core.Api";
private readonly string version = "V1";
public void Configure(IApplicationBuilder app, IHostingEnvironment env){if (env.IsDevelopment()){app.UseDeveloperExceptionPage();}app.UseCors("LimitRequests");//注册异常处理中间件app.UseMiddleware<GlobalExceptionMiddleware>();#region Swaggerapp.UseSwagger();app.UseSwaggerUI(c =>{c.SwaggerEndpoint($"/swagger/{version}/swagger.json", $"{ApiName} {version}");//c.IndexStream = () => GetType().GetTypeInfo().Assembly.GetManifestResourceStream("index.html");c.RoutePrefix = "";});#endregion// 开启权限app.UseAuthentication();app.UseMvc();}
        public IServiceProvider ConfigureServices(IServiceCollection services){services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);services.AddCors(c => {c.AddPolicy("LimitRequests", policy => {policy//.AllowAnyOrigin().WithOrigins(new string[]{ "http://localhost:8881" })//.WithOrigins(Appsettings.app(new string[] { "Startup", "Cors", "IPs" }).Split(',')).AllowAnyHeader().AllowAnyMethod();});});#region Swagger UI Servicevar basePath = Microsoft.DotNet.PlatformAbstractions.ApplicationEnvironment.ApplicationBasePath;services.AddSwaggerGen(c =>{//遍历出全部的版本,做文档信息展示c.SwaggerDoc(version, new OpenApiInfo{Version = version,Title = $"{ApiName} 接口文档——{RuntimeInformation.FrameworkDescription}",Description = $"{ApiName} HTTP API " + version,Contact = new OpenApiContact { Name = ApiName, Email = "nmmking.Core@xxx.com", Url = new Uri("http地址") },License = new OpenApiLicense { Name = ApiName + " 官方文档", Url = new Uri("http地址") }});c.OrderActionsBy(o => o.RelativePath);//就是这里var xmlPath = Path.Combine(basePath, "nmmking.Core.Api.xml");//这个就是刚刚配置的xml文件名c.IncludeXmlComments(xmlPath, true);//默认的第二个参数是false,这个是controller的注释,记得修改var xmlPath2 = Path.Combine(basePath, "nmmking.Core.Model.xml");c.IncludeXmlComments(xmlPath2, true);#region Token绑定到ConfigureServices// 开启加权小锁c.OperationFilter<AddResponseHeadersFilter>();c.OperationFilter<AppendAuthorizeToSummaryOperationFilter>();// 在header中添加token,传递到后台c.OperationFilter<SecurityRequirementsOperationFilter>();// Jwt Bearer 认证,必须是 oauth2c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme{Description = "JWT授权(数据将在请求头中进行传输) 直接在下框中输入Bearer {token}(注意两者之间是一个空格)\"",Name = "Authorization",//jwt默认的参数名称In = ParameterLocation.Header,//jwt默认存放Authorization信息的位置(请求头中)Type = SecuritySchemeType.ApiKey});#endregion});#endregion#region 授权// 读取配置文件var audienceConfig = Configuration.GetSection("Audience");var symmetricKeyAsBase64 = audienceConfig["Secret"];var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);var signingKey = new SymmetricSecurityKey(keyByteArray);var signingCredentials = new SigningCredentials(signingKey, SecurityAlgorithms.HmacSha256); //签名// 角色与接口的权限要求参数var permissionRequirement = new PermissionRequirement(ClaimTypes.Role,//基于角色的授权audienceConfig["Issuer"],//发行人audienceConfig["Audience"],//听众signingCredentials,//签名凭据expiration: TimeSpan.FromSeconds(60 * 60)//接口的过期时间);// 授权services.AddAuthorization(options =>{// 权限变量配置options.AddPolicy("Permission",policy => policy.Requirements.Add(permissionRequirement));});// 令牌验证参数var tokenValidationParameters = new TokenValidationParameters{ValidateIssuerSigningKey = true,IssuerSigningKey = signingKey,ValidateIssuer = true,ValidIssuer = audienceConfig["Issuer"],//发行人ValidateAudience = true,ValidAudience = audienceConfig["Audience"],//订阅人ValidateLifetime = true,ClockSkew = TimeSpan.FromSeconds(30),RequireExpirationTime = true,};services.AddAuthentication("Bearer").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;}};});services.AddSingleton(permissionRequirement);#endregionreturn services.BuildDynamicProxyProvider();}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/625435.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

还有同学开题报告没写吗?

引言 作为一名在软件技术领域深耕多年的专业人士&#xff0c;我不仅在软件开发和项目部署方面积累了丰富的实践经验&#xff0c;更以卓越的技术实力获得了&#x1f3c5;30项软件著作权证书的殊荣。这些成就不仅是对我的技术专长的肯定&#xff0c;也是对我的创新精神和专业承诺…

微信小程序全局配置

全局配置文件及常用的配置项 小程序根目录下的 app.json 文件是小程序的全局配置文件。常用的配置项如下&#xff1a; ① pages 记录当前小程序所有页面的存放路径 ② window 全局设置小程序窗口的外观 ③ tabBar 设置小程序底部的 tabBar 效果 ④ style 是否启用新版的组件样…

uniapp--登录和注册页面-- login

目录 1.效果展示 2.源代码展示 测试登录 login.js 测试请求 request.js 测试首页index.js 1.效果展示 2.源代码展示 <template><view><f-navbar title"登录" navbarType"4"></f-navbar><view class"tips"><…

初识--Linux的虚拟地址空间

重新了解地址空间 在学习c/c语言的时候,大家一定见过以下这张图 说的是程序会加载在如图的结构上,实际上,我们真的对他很了解吗,而在Linux进程控制这,就会有一个奇怪的现象 前提提要:简要介绍一下fork函数 进程内核数据结构(PCB)自己的代码以及数据 在Linux中,fork可以从当…

Sy-linux下常用的网络命令linux network commands

linux下的网络命令非常强大&#xff0c;这里根据教材需要&#xff0c;列出来常用的网络命令和场景实例&#xff0c;供参考。 一、命令列表&#xff1a; Command Description ip Manipulating routing to assigning and configuring network parameters traceroute Identi…

RabbitMQ Stream插件使用详解

2.4版为RabbitMQ流插件引入了对RabbitMQStream插件Java客户端的初始支持。 RabbitStreamTemplateStreamListener容器 将spring rabbit流依赖项添加到项目中&#xff1a; <dependency><groupId>org.springframework.amqp</groupId><artifactId>sprin…

Java工程师常见面试题:Java基础(一)

1、JDK 和 JRE 有什么区别&#xff1f; JDK是Java开发工具包&#xff0c;它包含了JRE和开发工具&#xff08;如javac编译器和java程序运行工具等&#xff09;&#xff0c;主要用于Java程序的开发。而JRE是Java运行环境&#xff0c;它只包含了运行Java程序所必须的环境&#xf…

Java springboot使用EasyExcel读Excel文件,映射不到属性值,对象属性值都是null

如果你的类上有这个注解&#xff0c;去掉火或注释掉就可以了 Accessors(chain true)解决方法

vue 常用的日历排班,带农历显示组件(2024-04-16)

显示当前月日历组件&#xff0c;里面带农历或节日显示 后面可以丰富一些国家法定节假期的业务需求 代码 js-calendar.js 文件 var lunarInfo [0x04bd8, 0x04ae0, 0x0a570, 0x054d5, 0x0d260, 0x0d950, 0x16554, 0x056a0, 0x09ad0, 0x055d2, //1900-19090x04ae0, 0x0a5b6, 0…

【大数据】分布式文件系统HDFS

目录 1.什么是分布式文件系统 2.HDFS的特点 3.HDFS的核心概念 4.HDFS的体系结构 5.HDFS的配置建议 6.HDFS的局限性 7.HDFS的存储机制 7.1.数据冗余机制 7.2.错误与恢复 8.HDFS数据读写过程 1.什么是分布式文件系统 分布式文件系统是整个大数据技术的基础&#xff0c…

常用序号、标点符号 相关正则表达式

(?:[\(|&#xff08;|\[])?\d[\]|\)&#xff09;|\、]|[\u2460-\u2473]|[\u4e00-\u5341][.|、]匹配序号 \d\.(?!\d)|\d、常规序号匹配&#xff1a; rule1: 标准格式1. 2、 rule2:排除小数 [^\u4E00-\u9FA5\uFF00-\uFFEFa-zA-Z0-9\s]所有符号 [\u3000-\u303F\uFF00-\uFFE…

STM32 软件I2C方式读取MT6701磁编码器获取角度例程

STM32 软件I2C方式读取MT6701磁编码器获取角度例程 &#x1f4cd;相关篇《STM32 软件I2C方式读取AS5600磁编码器获取角度例程》&#x1f33f;《Arduino通过I2C驱动MT6701磁编码器并读取角度数据》&#x1f530;MT6701芯片和AS5600从软件读取对比&#xff0c;只是读取的寄存器和…