Quartz.NET调用REST API新增Job出现Value cannot be null或Job类为空错误
问题根因
你遇到的两个报错本质上是两个独立的参数问题:
- 第一个
Value cannot be null. (Parameter 'typeName')报错:是因为ASP.NET Core参数绑定规则问题,你将jobType等参数放在请求Body中传递,但控制器方法没有配置从Body读取参数,导致jobType参数接收到的值为null,调用Type.GetType时触发空参数异常。 - 第二个
Job class cannot be null报错:是因为Type.GetType方法需要传入带命名空间和程序集的完整类型限定名才能正确识别类型,你只传了HelloWorldJob类名,GetType返回null,赋值给JobType时触发异常。同时你之前的路由配置漏了schedulerName参数,也会导致参数取值异常。
解决步骤
1. 修复控制器参数绑定
推荐将Body参数封装为请求DTO,明确配置[FromBody]绑定,同时补全路由参数:
首先定义请求DTO类:
public class AddJobRequest { public string JobType { get; set; } public bool Durable { get; set; } public bool RequestsRecovery { get; set; } public bool Replace { get; set; } }
修改控制器方法:
[HttpPut] [Route("{schedulerName}/{jobGroup}/{jobName}")] public async Task AddJob(string schedulerName, string jobGroup, string jobName, [FromBody] AddJobRequest request) { var scheduler = await GetScheduler(schedulerName).ConfigureAwait(false); // 提前校验Job类型合法性 var jobType = Type.GetType(request.JobType); if (jobType == null) { throw new ArgumentException($"无效的Job类型:{request.JobType}"); } var jobDetail = new JobDetailImpl(jobName, jobGroup, jobType, request.Durable, request.RequestsRecovery); await scheduler.AddJob(jobDetail, request.Replace).ConfigureAwait(false); }
2. 修正前端传参
- 补全请求URL的
schedulerName参数,默认Quartz调度器名称为Default - 将
jobType改为完整类型限定名,格式为{类型完整命名空间}.{类名}, {程序集名称},例如你的HelloWorldJob放在QuartzApi.Jobs命名空间下,项目程序集名为QuartzApi,则传值为QuartzApi.Jobs.HelloWorldJob, QuartzApi
修改后的前端JS代码:
function addJob() { const addNameInput = document.getElementById('add-name'); const addNameText = addNameInput.value.trim(); const item = { jobType: "QuartzApi.Jobs.HelloWorldJob, QuartzApi", // 替换为你实际的Job完整限定名 durable: true, requestsRecovery: false, replace: false }; // 补全schedulerName参数,格式为 调度器名/分组/Job名 fetch(`${uri}/Default/DEFAULT/${addNameText}`, { method: 'PUT', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify(item) }) .then(response => console.log(response)) .then(() => { getJobs(); addNameInput.value = ''; }) .catch(error => console.error('Unable to add job.', error)); }
可选优化:简化前端传参
如果不想暴露后端类型命名空间,可以在后端提前缓存所有Job实现类,做短名映射:
// 全局缓存所有实现了IJob的非抽象类型 private static readonly Dictionary<string, Type> JobTypeMapping = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(assembly => assembly.GetTypes()) .Where(type => typeof(IJob).IsAssignableFrom(type) && !type.IsAbstract) .ToDictionary(type => type.Name, type => type); // 控制器中替换GetType逻辑 if (!JobTypeMapping.TryGetValue(request.JobType, out var jobType)) { throw new ArgumentException($"不存在的Job类型:{request.JobType}"); }
配置后前端可以直接传jobType: "HelloWorldJob"即可正常识别。
内容的提问来源于stack exchange,提问作者Amit
相关产品推荐
相关产品推荐

