Quartz.NET官网地址:https://www.quartz-scheduler.net/

Quartz.NET文档地址:https://www.quartz-scheduler.net/documentation/index.html

Quartz.NET是一个开源的作业调度框架,是OpenSymphonyQuartz API的.NET移植,它用C#写成,可用于winformasp.net应用中。它提供了巨大的灵活性而不牺牲简单性。你能够用它来为执行一个作业而创建简单的或复杂的调度。它有很多特征,如:数据库支持,集群,插件,支持cron-like表达式等等。

现在Quartz.NET3.0已支持Asp.Net Core,3.0新功能如下:

新功能

  • 具有异步/等待支持的基于任务的作业,内部以异步/等待方式工作
  • 支持.NET Core / netstandard 2.0和.NET Framework 4.5.2及更高版本
  • 通过提供程序名称SQLite-Microsoft支持Microsoft.Data.Sqlite,旧的提供程序SQLite也仍然有效
  • 增加了SQL Server内存优化表和Quartz.Impl.AdoJobStore.UpdateLockRowSemaphoreMOT的初步支持
  • Common.Logging从相关性中删除
  • ILMerge进程中删除的C5集合不再需要
  • 在插件启动时添加对作业调度XML文件的急切验证的支持
  • TimeZoneUtil中添加对额外的自定义时区解析器功能的支持

变化

  • 作业和插件现在位于独立的程序集NuGetQuartz.JobsQuartz.Plugins
  • ADO.NET提供者名称已被简化,提供者名称没有版本,例如SqlServer-20 => SqlServer
  • API方法已被重新使用,主要使用IReadOnlyCollection,这隐藏了两个HashSets和List小号
  • LibLog一直隐藏于内部(ILog等),就像它原本打算的那样
  • SimpleThreadPool消失了,旧的拥有的线程消失了
  • 调度程序方法已更改为基于任务,请记住等待它们
  • IJob接口现在返回一个任务
  • 一些IList属性已更改为IReadOnlyList以正确反映意图
  • SQL Server CE支持已被删除
  • DailyCalendar现在将日期时间用于排除的日期,并具有ISet接口来访问它们
  • IObjectSerializer有新的方法,void Initialize(),必须实现
  • IInterruptableJob取消了上下文的CancellationToken

Quartz API的关键接口和类是

  • IScheduler - 与调度程序交互的主要API。
  • IJob - 您希望由调度程序执行的组件实现的接口。
  • IJobDetail - 用于定义作业的实例。
  • ITrigger - 定义执行给定Job的时间表的组件。
  • JobBuilder - 用于定义/构建定义作业实例的JobDetail实例。
  • TriggerBuilder - 用于定义/构建触发器实例

一、Quartz.NET基本使用

1、新建Asp.Net Core 项目,使用NuGet添加Quartz,或使用程序包管理器引用,命令如下:
Install-Package Quartz

如果你想添加JSON序列化,只需要以同样的方式添加Quartz.Serialization.Json包。

2、简单实例,代码如下:
using Five.QuartzNetJob.ExecuteJobTask.Service;
using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Text;
using System.Threading.Tasks; namespace Five.QuartzNetJob.Web.Controllers
{
public class TestTask
{
public async Task StartTestAsync()
{
try
{
// 从工厂中获取调度程序实例
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" }
};
StdSchedulerFactory factory = new StdSchedulerFactory(props);
IScheduler scheduler = await factory.GetScheduler(); // 开启调度器
await scheduler.Start(); // 定义这个工作,并将其绑定到我们的IJob实现类
IJobDetail job = JobBuilder.Create<HelloJob>()
.WithIdentity("job1", "group1")
.Build(); // 触发作业立即运行,然后每10秒重复一次,无限循环
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartNow()
.WithSimpleSchedule(x => x
.WithIntervalInSeconds()
.RepeatForever())
.Build(); // 告诉Quartz使用我们的触发器来安排作业
await scheduler.ScheduleJob(job, trigger); // 等待60秒
await Task.Delay(TimeSpan.FromSeconds()); // 关闭调度程序
await scheduler.Shutdown();
}
catch (SchedulerException se)
{
await Console.Error.WriteLineAsync(se.ToString());
}
}
}
}

HelloJob内容如下:

using Quartz;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks; namespace Five.QuartzNetJob.ExecuteJobTask.Service
{
public class HelloJob : IJob
{
public Task Execute(IJobExecutionContext context)
{
Console.Out.WriteLineAsync("Greetings from HelloJob!");
return Task.CompletedTask;
}
}
}

执行效果如下:

注:Quartz的版本3.0.3中删除了IJob实现的异地调用,也就是不支持asyncawait异步调用,3.0.2版本支持异步调用。

二、触发器类型

1、SimpleTrigger触发器(简单触发器)

SimpleTrigger的属性包括:开始时间和结束时间,重复计数和重复间隔。重复计数可以是零,一个正整数或常数值SimpleTrigger.RepeatIndefinitely。重复时间间隔属性必须是TimeSpan.Zero或正的TimeSpan值。请注意,重复间隔为0会导致触发器的“重复计数”触发同时发生。

SimpleTrigger实例使用TriggerBuilder(用于触发器的主属性)和WithSimpleSchedule扩展方法(用于SimpleTrigger特定的属性)构建。

在特定的时间内建立触发器,无需重复,代码如下:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ISimpleTrigger trigger = (ISimpleTrigger)TriggerBuilder.Create()
.WithIdentity("trigger1", "group1")
.StartAt(DateTime.Now) //指定开始时间为当前系统时间
.ForJob("job1", "group1") //通过JobKey识别作业
.Build();

在特定的时间建立触发器,然后每十秒钟重复十次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = trigger = TriggerBuilder.Create()
.WithIdentity("trigger2", "group2")
.StartAt(DateTime.Now) // 指定开始时间
.WithSimpleSchedule(x => x
.WithIntervalInSeconds()
.WithRepeatCount()) // 请注意,重复10次将总共重复11次
.ForJob("job2", "group2") //通过JobKey识别作业
.Build();

构建一个触发器,将在未来五分钟内触发一次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = trigger = (ISimpleTrigger)TriggerBuilder.Create()
.WithIdentity("trigger3", "group3")
.StartAt(DateBuilder.FutureDate(, IntervalUnit.Minute)) //使用DateBuilder将来创建一个时间日期
.ForJob("job3", "group3") //通过JobKey识别作业
.Build();

建立一个现在立即触发的触发器,然后每隔五分钟重复一次,直到22:00:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = trigger = TriggerBuilder.Create()
.WithIdentity("trigger4", "group4")
.WithSimpleSchedule(x => x
.WithIntervalInMinutes()//每5秒执行一次
.RepeatForever())
.EndAt(DateBuilder.DateOf(, , ))//晚上22点结束
.Build();

建立一个触发器,在一个小时后触发,然后每2小时重复一次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("trigger5") // 由于未指定组,因此“trigger5”将位于默认分组中
.StartAt(DateBuilder.EvenHourDate(null)) // 获取下个一小时时间
.WithSimpleSchedule(x => x
.WithIntervalInHours()//执行间隔2小时
.RepeatForever())
.Build(); 

因此简单的任务调度使用SimpleTrigger完全够用,如果SimpleTrigger还是不能满足您的需求请往下看。

2、CronTrigger触发器

如果你需要一个基于类似日历的概念而不是精确指定的SimpleTrigger时间间隔的工作调度计划,CronTriggers通常比SimpleTrigger更有用。

使用CronTrigger,您可以在每周一,周三的上午9点至上午10点之间指定开始时间表,例如“每星期五中午”或“每个工作日和上午9点30分”,或者“每5分钟”和星期五”。

即使如此,就像SimpleTrigger一样,CronTrigger有一个startTime,它指定了时间表的生效时间,还有一个(可选的)endTime,用于指定应该停止时间表的时间。

这里不在详细介绍Cron

Cron表达式在线生成器:http://cron.qqe2.com/

Cron表达式详细介绍:https://www.jianshu.com/p/e9ce1a7e1ed1

每天早上8点到下午5点建立一个触发器,每隔一分钟就会触发一次:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("Job1", "group1")
.WithCronSchedule("0 0/2 8-17 * * ?")//使用Cron表达式
.ForJob("Job1", "group1")
.Build();

建立一个触发器,每天在上午10:42开始执行:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("Job2", "group2")
.WithSchedule(CronScheduleBuilder.DailyAtHourAndMinute(, )) // 在这里使用CronScheduleBuilder的静态辅助方法
.ForJob("Job2", "group2")
.Build();

构建一个触发器,将在星期三上午10:42在除系统默认值之外的TimeZone中触发:

// 触发器构建器默认创建一个简单的触发器,实际上返回一个ITrigger
ITrigger trigger = TriggerBuilder.Create()
.WithIdentity("Job3", "group3")
.WithCronSchedule("0 42 10 ? * WED", x => x
.InTimeZone(TimeZoneInfo.FindSystemTimeZoneById("Central America Standard Time")))
.ForJob("Job3", "group3")
.Build();

总结: Quartz.NET的3.0版本跟之前的版本api接口变化并不大。只是在3.0版本中添加了异步调用,并支持.net core。简单的任务调度使用官网中的实例即可满足需求。

三、后端项目管理

在网上找到很多关于asp.net 的任务管理,但没有找到.net core 相关的后端任务管理。

我就根据官网还有网上dalao开源分享的项目实现了个简单的后端任务。

项目地址:https://github.com/Potato-MC/QuartzNetJob

项目效果图请看:http://www.cnblogs.com/miskis/p/8484252.html

项目结构

项目使用asp.net core 2.0--->adminlte-2.4.2-->SqlSugar-4.6.4.3。

数据库表可使用SqlSugar来生成。

public IActionResult Index()
{
//生成表
//var db = DataHelper.GetInstance();
//db.CodeFirst.InitTables(typeof(OperateLog), typeof(OperateLog));
return View();
}

结构如下:

Five.QuartzNetJob.DataService.DataHelper----------------------------------是ORM层,使用的是开源框架SqlSugar(官网地址:http://www.codeisbug.com/Doc/8

Five.QuartzNetJob.DataService.Models---------------------------------------是实体类

Five.QuartzNetJob.ExecuteJobTask.Service---------------------------------IJob实现层

QuartzNet.Entity---------------------------------------------------------------------调度中心相关实体类

QuartzNet2.Core--------------------------------------------------------------------非.Net Core版本的调度管理中心,使用的是.net framework 4.6

Five.QuartzNetJob.Utils.Tool-----------------------------------------------------通用工具类库

Five.QuartzNetJob.Web-----------------------------------------------------------后端

项目很简单,就只实现了增删改查。

统一管理任务调度,项目运行时开启任务调度并执行已开启的任务。

因为项目太过于简单,就不在详细介绍。

下面贴出任务调度中心代码,欢迎各位dalao发表意见:

using QuartzNet.Entity;
using Quartz;
using Quartz.Impl;
using System;
using System.Collections.Specialized;
using System.Threading.Tasks;
using Five.QuartzNetJob.Utils.Tool;
using System.Reflection;
using System.Collections.Generic; namespace QuartzNet3.Core
{
/// <summary>
/// 任务调度中心
/// </summary>
public class SchedulerCenter
{
/// <summary>
/// 任务调度对象
/// </summary>
public static readonly SchedulerCenter Instance;
static SchedulerCenter()
{
Instance = new SchedulerCenter();
}
private Task<IScheduler> _scheduler; /// <summary>
/// 返回任务计划(调度器)
/// </summary>
/// <returns></returns>
private Task<IScheduler> Scheduler
{
get
{
if (this._scheduler != null)
{
return this._scheduler;
}
// 从Factory中获取Scheduler实例
NameValueCollection props = new NameValueCollection
{
{ "quartz.serializer.type", "binary" },
//以下配置需要数据库表配合使用,表结构sql地址:https://github.com/quartznet/quartznet/tree/master/database/tables
//{ "quartz.jobStore.type","Quartz.Impl.AdoJobStore.JobStoreTX, Quartz"},
//{ "quartz.jobStore.driverDelegateType","Quartz.Impl.AdoJobStore.StdAdoDelegate, Quartz"},
//{ "quartz.jobStore.tablePrefix","QRTZ_"},
//{ "quartz.jobStore.dataSource","myDS"},
//{ "quartz.dataSource.myDS.connectionString",AppSettingHelper.MysqlConnection},//连接字符串
//{ "quartz.dataSource.myDS.provider","MySql"},
//{ "quartz.jobStore.useProperties","true"} };
StdSchedulerFactory factory = new StdSchedulerFactory(props);
return this._scheduler = factory.GetScheduler();
}
} /// <summary>
/// 运行指定的计划(映射处理IJob实现类)
/// </summary>
/// <param name="jobGroup">任务分组</param>
/// <param name="jobName">任务名称</param>
/// <returns></returns>
public async Task<BaseQuartzNetResult> RunScheduleJob<T>(string jobGroup, string jobName) where T : ScheduleManage
{
BaseQuartzNetResult result;
//开启调度器
await this.Scheduler.Result.Start();
//创建指定泛型类型参数指定的类型实例
T t = Activator.CreateInstance<T>();
//获取任务实例
ScheduleEntity scheduleModel = t.GetScheduleModel(jobGroup, jobName);
//添加任务
var addResult = AddScheduleJob(scheduleModel).Result;
if (addResult.Code == )
{
scheduleModel.Status = EnumType.JobStatus.已启用;
t.UpdateScheduleStatus(scheduleModel);
//用给定的密钥恢复(取消暂停)IJobDetail
await this.Scheduler.Result.ResumeJob(new JobKey(jobName, jobGroup));
result = new BaseQuartzNetResult
{
Code = ,
Msg = "启动成功"
};
}
else
{
result = new BaseQuartzNetResult
{
Code = -
};
}
return result;
}
/// <summary>
/// 添加一个工作调度(映射程序集指定IJob实现类)
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
private async Task<BaseQuartzNetResult> AddScheduleJob(ScheduleEntity m)
{
var result = new BaseQuartzNetResult();
try
{ //检查任务是否已存在
var jk = new JobKey(m.JobName, m.JobGroup);
if (await this.Scheduler.Result.CheckExists(jk))
{
//删除已经存在任务
await this.Scheduler.Result.DeleteJob(jk);
}
//反射获取任务执行类
var jobType = FileHelper.GetAbsolutePath(m.AssemblyName, m.AssemblyName + "." + m.ClassName);
// 定义这个工作,并将其绑定到我们的IJob实现类
IJobDetail job = new JobDetailImpl(m.JobName, m.JobGroup, jobType);
//IJobDetail job = JobBuilder.CreateForAsync<T>().WithIdentity(m.JobName, m.JobGroup).Build();
// 创建触发器
ITrigger trigger;
//校验是否正确的执行周期表达式
if (!string.IsNullOrEmpty(m.Cron) && CronExpression.IsValidExpression(m.Cron))
{
trigger = CreateCronTrigger(m);
}
else
{
trigger = CreateSimpleTrigger(m);
} // 告诉Quartz使用我们的触发器来安排作业
await this.Scheduler.Result.ScheduleJob(job, trigger); result.Code = ;
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(string.Format("添加任务出错{0}", ex.Message));
result.Code = ;
result.Msg = ex.Message;
}
return result;
}
/// <summary>
/// 运行指定的计划(泛型指定IJob实现类)
/// </summary>
/// <param name="jobGroup">任务分组</param>
/// <param name="jobName">任务名称</param>
/// <returns></returns>
public async Task<BaseQuartzNetResult> RunScheduleJob<T, V>(string jobGroup, string jobName) where T : ScheduleManage, new() where V : IJob
{
BaseQuartzNetResult result;
//开启调度器
await this.Scheduler.Result.Start();
//创建指定泛型类型参数指定的类型实例
T t = Activator.CreateInstance<T>();
//获取任务实例
ScheduleEntity scheduleModel = t.GetScheduleModel(jobGroup, jobName);
//添加任务
var addResult = AddScheduleJob<V>(scheduleModel).Result;
if (addResult.Code == )
{
scheduleModel.Status = EnumType.JobStatus.已启用;
t.UpdateScheduleStatus(scheduleModel);
//用给定的密钥恢复(取消暂停)IJobDetail
await this.Scheduler.Result.ResumeJob(new JobKey(jobName, jobGroup));
result = new BaseQuartzNetResult
{
Code = ,
Msg = "启动成功"
};
}
else
{
result = new BaseQuartzNetResult
{
Code = -
};
}
return result;
}
/// <summary>
/// 添加任务调度(指定IJob实现类)
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="m"></param>
/// <returns></returns>
private async Task<BaseQuartzNetResult> AddScheduleJob<T>(ScheduleEntity m) where T : IJob
{
var result = new BaseQuartzNetResult();
try
{ //检查任务是否已存在
var jk = new JobKey(m.JobName, m.JobGroup);
if (await this.Scheduler.Result.CheckExists(jk))
{
//删除已经存在任务
await this.Scheduler.Result.DeleteJob(jk);
}
//反射获取任务执行类
// var jobType = FileHelper.GetAbsolutePath(m.AssemblyName, m.AssemblyName + "." + m.ClassName);
// 定义这个工作,并将其绑定到我们的IJob实现类
//IJobDetail job = new JobDetailImpl(m.JobName, m.JobGroup, jobType);
IJobDetail job = JobBuilder.CreateForAsync<T>().WithIdentity(m.JobName, m.JobGroup).Build();
// 创建触发器
ITrigger trigger;
//校验是否正确的执行周期表达式
if (!string.IsNullOrEmpty(m.Cron) && CronExpression.IsValidExpression(m.Cron))
{
trigger = CreateCronTrigger(m);
}
else
{
trigger = CreateSimpleTrigger(m);
} // 告诉Quartz使用我们的触发器来安排作业
await this.Scheduler.Result.ScheduleJob(job, trigger); result.Code = ;
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(string.Format("添加任务出错", ex.Message));
result.Code = ;
result.Msg = ex.Message;
}
return result;
}
/// <summary>
/// 暂停指定的计划
/// </summary>
/// <param name="jobGroup">任务分组</param>
/// <param name="jobName">任务名称</param>
/// <param name="isDelete">停止并删除任务</param>
/// <returns></returns>
public BaseQuartzNetResult StopScheduleJob<T>(string jobGroup, string jobName, bool isDelete = false) where T : ScheduleManage, new()
{
BaseQuartzNetResult result;
try
{
this.Scheduler.Result.PauseJob(new JobKey(jobName, jobGroup));
if (isDelete)
{
Activator.CreateInstance<T>().RemoveScheduleModel(jobGroup, jobName);
}
result = new BaseQuartzNetResult
{
Code = ,
Msg = "停止任务计划成功!"
};
}
catch (Exception ex)
{
result = new BaseQuartzNetResult
{
Code = -,
Msg = "停止任务计划失败"
};
}
return result;
}
/// <summary>
/// 恢复运行暂停的任务
/// </summary>
/// <param name="jobName">任务名称</param>
/// <param name="jobGroup">任务分组</param>
public async void ResumeJob(string jobName, string jobGroup)
{
try
{
//检查任务是否存在
var jk = new JobKey(jobName, jobGroup);
if (await this.Scheduler.Result.CheckExists(jk))
{
//任务已经存在则暂停任务
await this.Scheduler.Result.ResumeJob(jk);
await Console.Out.WriteLineAsync(string.Format("任务“{0}”恢复运行", jobName));
}
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(string.Format("恢复任务失败!{0}", ex));
}
} /// <summary>
/// 停止任务调度
/// </summary>
public async void StopScheduleAsync()
{
try
{
//判断调度是否已经关闭
if (!this.Scheduler.Result.IsShutdown)
{
//等待任务运行完成
await this.Scheduler.Result.Shutdown();
await Console.Out.WriteLineAsync("任务调度停止!");
}
}
catch (Exception ex)
{
await Console.Out.WriteLineAsync(string.Format("任务调度停止失败!", ex));
}
}
/// <summary>
/// 创建类型Simple的触发器
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
private ITrigger CreateSimpleTrigger(ScheduleEntity m)
{
//作业触发器
if (m.RunTimes > )
{
return TriggerBuilder.Create()
.WithIdentity(m.JobName, m.JobGroup)
.StartAt(m.BeginTime)//开始时间
.EndAt(m.EndTime)//结束数据
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(m.IntervalSecond)//执行时间间隔,单位秒
.WithRepeatCount(m.RunTimes))//执行次数、默认从0开始
.ForJob(m.JobName, m.JobGroup)//作业名称
.Build();
}
else
{
return TriggerBuilder.Create()
.WithIdentity(m.JobName, m.JobGroup)
.StartAt(m.BeginTime)//开始时间
.EndAt(m.EndTime)//结束数据
.WithSimpleSchedule(x => x
.WithIntervalInSeconds(m.IntervalSecond)//执行时间间隔,单位秒
.RepeatForever())//无限循环
.ForJob(m.JobName, m.JobGroup)//作业名称
.Build();
} }
/// <summary>
/// 创建类型Cron的触发器
/// </summary>
/// <param name="m"></param>
/// <returns></returns>
private ITrigger CreateCronTrigger(ScheduleEntity m)
{
// 作业触发器
return TriggerBuilder.Create()
.WithIdentity(m.JobName, m.JobGroup)
.StartAt(m.BeginTime)//开始时间
.EndAt(m.EndTime)//结束时间
.WithCronSchedule(m.Cron)//指定cron表达式
.ForJob(m.JobName, m.JobGroup)//作业名称
.Build();
}
}
}

总结:

开发已个小项目搞了好久才搞完,期间零零散散的开发,还是太懒散了!

平时积累不够,还是太菜了!!!!!

Asp.Net Core2.0 基于QuartzNet任务管理系统的更多相关文章

  1. Net Core2.0 基于QuartzNet任务管理系统

    Net Core2.0 基于QuartzNet任务管理系统 Quartz.NET官网地址:https://www.quartz-scheduler.net/ Quartz.NET文档地址:https: ...

  2. 【翻译】asp.net core2.0中的token认证

    原文地址:https://developer.okta.com/blog/2018/03/23/token-authentication-aspnetcore-complete-guide token ...

  3. 将asp.net core2.0项目部署在IIS上运行

    原文:将asp.net core2.0项目部署在IIS上运行 前言:  与ASP.NET时代不同,ASP.NET Core不再是由IIS工作进程(w3wp.exe)托管,而是独立运行的.它独立运行在控 ...

  4. 一步一步带你做WebApi迁移ASP.NET Core2.0

    随着ASP.NET Core 2.0发布之后,原先运行在Windows IIS中的ASP.NET WebApi站点,就可以跨平台运行在Linux中.我们有必要先说一下ASP.NET Core. ASP ...

  5. Asp.net Core2.0 缓存 MemoryCache 和 Redis

    自从使用Asp.net Core2.0 以来,不停摸索,查阅资料,这方面的资料是真的少,因此,在前人的基础上,摸索出了Asp.net Core2.0 缓存 MemoryCache 和 Redis的用法 ...

  6. 【转】Asp.Net Core2.0获取客户IP地址,及解决发布到Ubuntu服务器获取不到正确IP解决办法

    1.获取客户端IP地址实现方法(扩展类) using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ModelBinding; u ...

  7. [翻译]在asp.net core2.0 OpenID Connect Handler中丢失了声明(CLaims)?

    注:这是一篇翻译,来自这里.这篇文章讲述了在asp.net core2.0中使用openid connect handler的过程中解析不到你想要的claim时,你可以参考这篇文章. Missing ...

  8. Asp.Net Core2.0获取客户IP地址,及解决发布到Ubuntu服务器获取不到正确IP解决办法

    1.获取客户端IP地址实现方法(扩展类) using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc.ModelBinding; u ...

  9. VS2017创建一个 ASP.NET Core2.0 应用,并搭建 MVC 框架

    https://testerhome.com/topics/11747 1.使用最新版本的VS2017,并安装.NET Core2.0中相关开发工具   2.打开VS2017,点击文件-新建-项目,选 ...

随机推荐

  1. Python+Selenium安装及环境配置

    一.Python安装 Window系统下,python的安装很简单.访问python.org/download,下载最新版本,安装过程与其他windows软件类似.记得下载后设置path环境变量,然后 ...

  2. mybatis-pageHelper做分页

    Mybatis-PageHelpera是一个很好的第三方分页插件,支持很多数据库,几乎主流的数据库都支持 github地址:https://github.com/pagehelper/Mybatis- ...

  3. Hi,WeTest限免开放Android Oreo云真机,Android 8.1可开测!

    2017年末,谷歌在印度正式发布 Android Oreo 8.1,向实现"为所有人打造由 AI 驱动的全覆盖移动平台"这一愿景迈进.Android 8.1在引入对 Android ...

  4. chroot: failed to run command `/bin/bash': No such file or directory

    1 使用chroot命令时报错如下: testupgrade:/ # chroot /sb chroot: cannot change root directory to /sb: No such f ...

  5. java1.8--Null Object模式

    整理这篇博客是因为现在在整理java8中的optional,所以觉得很有必要整理下Null Object模式.java.lang.NullPointerException,只要敢自称Java程序员,那 ...

  6. 关于 tomcat 配置时遇到的问题与警告及解决办法

    首先,我们在日常配置 tomcat 时,总是会遇到这样的问题: 有时候我们会重新头来配置 tomcat,但是现在我们并不需要那么做,方法很简单,请继续往下看: 这个问题是告诉我们 tomcat 在 4 ...

  7. JAVA中的数据存储空间简述

    在 JAVA 中,有六个不同的地方可以存储数据: 1. 寄存器( register ): 最快的存储区,因为它位于不同于其他存储区——处理器内部.但是寄存器的数量极其有限,所以寄存器由编译器根据需求进 ...

  8. wpf datagrid row height 行高自动计算使每行行高自适应文本

    wpf 的datagrid的行高 要么是Auto,要么是定值:但会带来麻烦就是每行行高都一样. 当需要按内容(主要是wrap 换行的textbox或textblock)来动态调整行高的时候,需要用到d ...

  9. JSP基础篇

    JSP可以认为是加上了Java代码块的HTML文件,常常和CSS,JS结合使用,下面是一个JSP的基本的例子. <%@ page language="java" conten ...

  10. JavaBean命名规范

    ———————————————————————————————————————————————————————— 属性名/类型                    |                 ...