【问题标题】:When adding a Job to scheduler: Value cannot be null, Job class cannot be null?向调度器添加作业时:值不能为空,作业类不能为空?
【发布时间】:2021-10-21 20:31:31
【问题描述】:

我的问题非常类似于: Quartz.net - "Job's key cannot be null" 但是,当我使用 Rest API 时,它的设置不同。 通过 Startup.cs 添加时,我可以运行作业,但是当我调用 API 使用 javascript 添加作业时,它会失败并出现以下错误:

错误:

System.ArgumentNullException: Value cannot be null. (Parameter 'typeName')

   at System.RuntimeType.GetType(String typeName, Boolean throwOnError, Boolean ignoreCase, StackCrawlMark& stackMark)

   at System.Type.GetType(String typeName)

   at Quartz.Web.Api.JobsController.AddJob(String schedulerName, String jobGroup, String jobName, String jobType, Boolean durable, Boolean requestsRecovery, Boolean replace) in E:\Amit\DotNet\QuartzApi\QuartzApi\Controllers\JobsController.cs:line 108

   at lambda_method14(Closure , Object )

   at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.AwaitableResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()

--- End of stack trace from previous location ---

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|19_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)

   at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)

   at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)

   at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)

   at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)

 

HEADERS

=======

Accept: application/json

Accept-Encoding: gzip, deflate, br

Accept-Language: en-US,en;q=0.9

Connection: close

Content-Length: 83

Content-Type: application/json

Host: localhost:44379

Referer: https://localhost:44379/jobs.html

User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/92.0.4515.159 Safari/537.36

sec-ch-ua: "Chromium";v="92", " Not A;Brand";v="99", "Google Chrome";v="92"

sec-ch-ua-mobile: ?0

origin: https://localhost:44379

sec-fetch-site: same-origin

sec-fetch-mode: cors

sec-fetch-dest: empty

设置:

在 VS 中,我在一个项目中创建了 Quartz REST API 和前端。运行项目会加载网页,并在后台运行作业和 API。

除了 AddJob 之外的所有控制器端点都可以工作。 (即获取作业、查看作业详细信息、暂停、恢复、触发、删除)

依赖性: Quartz.Extensions.Hosting 3.3.3

JobsController.cs quartznet/JobsController.cs at main · quartznet/quartznet · GitHub

    [HttpPut]

    [Route("{jobGroup}/{jobName}")]

    public async Task AddJob(string schedulerName, string jobGroup, string jobName, string jobType, bool durable, bool requestsRecovery, bool replace = false)

    {

        var scheduler = await GetScheduler(schedulerName).ConfigureAwait(false);

        var jobDetail = new JobDetailImpl(jobName, jobGroup, Type.GetType(jobType), durable, requestsRecovery);

        await scheduler.AddJob(jobDetail, replace).ConfigureAwait(false);

    }

HelloWorldJob.cs: https://andrewlock.net/using-quartz-net-with-asp-net-core-and-worker-services/

Startup.cs:(添加没有 API 的作业并在启动时使用触发器运行它)

    void ConfigureHostQuartz(IServiceCollection services)

    {

        services.AddQuartz(q =>

        {

            q.UseMicrosoftDependencyInjectionScopedJobFactory();



            var jobKey = new JobKey("HelloWorldJob");

            q.AddJob<HelloWorldJob>(opts => opts.WithIdentity(jobKey));

            q.AddTrigger(opts => opts

                .ForJob(jobKey)

                .WithIdentity("HelloWorldJob-trigger")

                .WithCronSchedule("0/5 * * * * ?"));

        });



        services.AddQuartzHostedService(

            q => q.WaitForJobsToComplete = true);

    }

HTML/Javascript 前端: 按照这个例子: Tutorial: Call an ASP.NET Core web API with JavaScript | Microsoft Docs

<form action="javascript:void(0);" method="POST" onsubmit="addJob()">

    <input type="text" id="add-name" placeholder="New job">

    <input type="submit" value="Add">

</form>

<script>

function addJob() {

    const addNameTextbox = document.getElementById('add-name').value.trim();

 

    const item = {

        jobType: "HelloWorldJob",

        durable: true,

        requestsRecovery: false,

        replace: false

    };

 

    fetch(`${uri}/DEFAULT/${addNameTextbox}`, {

        method: 'PUT',

        headers: {

            'Accept': 'application/json',

            'Content-Type': 'application/json'

        },

        body: JSON.stringify(item)

    })

        .then(response => console.log(response))

        .then(() => {

            getJobs();

            addNameTextbox.value = '';

        })

        .catch(error => console.error('Unable to add job.', error));

}

</script>

我尝试更新 API 以在 url 中包含 jobType,然后它给出了不同的错误:

Job class cannot be null
at Qurtz.Impl.JobDetilImpl.set_JobType(Type value)

【问题讨论】:

    标签: javascript c# api quartz.net


    【解决方案1】:

    您需要提供程序集限定名称作为作业类型。问题就在这里:

    jobType: "HelloWorldJob",
    

    jobType 应该类似于 "MyNameSpace.JobType, MyAssembly" - 您可以使用 Console.WriteLine(typeof(HelloWorldJob).AssemblyQualifiedName) 将其写入控制台 - 您可以忽略版本等,只需要带有命名空间和程序集名称的类型名称。

    另请注意,您的设置具有安全隐患,因为您允许从 UI 传递 CLR 类型。

    【讨论】:

    • 感谢马尔科的回答。这并没有很好地安静。 API 的整个想法是在 UI 中提供 JobType(这是作业类名称)并让 API 弄清楚,它应该在 [... JobDetailImpl(... Type.GetType(jobType) ... ] 在进一步调整 API 中的 HttpPut 方法后,我发现在 API 中忽略了包含 jobType、durable 等传入 body 标记的 JSON 对象。Console.Writeline 没有输出任何内容。我能够找出原因并将在我的回答很快。但是您的意见非常有帮助,并激发了我进一步调查的兴趣!
    【解决方案2】:

    API 控制器更改:

    正如上面 Marko 所提到的,jobType 需要完全限定的名称,但在我的情况下,程序集引用不是必需的,因为我在同一个程序集中有工作。

        [HttpPut]
        [Route("{jobGroup}/{jobName}/{jobType}/{replace}/new")]
        public async Task NewJob(string schedulerName, string jobGroup,
            string jobName, string jobType, bool replace = false)
        {
            //Note: Job added without a trigger must be durable.
            var scheduler = await GetScheduler(schedulerName).ConfigureAwait(false);
            var jobDetail = new JobDetailImpl(jobName, jobGroup, 
                Type.GetType("QuartzApi.Jobs." + jobType), true, false);
            await scheduler.AddJob(jobDetail, replace).ConfigureAwait(false);
        }
    

    JavaScript 获取查询更改:

    删除了 JSON 正文标签并为 url 添加了额外的参数。请注意它是一项没有触发器的工作。在稍后阶段,jobType 可以是一个变量,现在它包含在 fetch 字符串中。

    function addJob() {
        const addNameTextbox = document.getElementById('add-name').value.trim();
    
        fetch(`${uri}/DEFAULT/${addNameTextbox}/HelloWorldJob/false/new`, {
            method: 'PUT',
            headers: {
                'Accept': 'application/json',
                'Content-Type': 'application/json'
            }
        })
            .then(response => console.log(response))
            .then(() => {
                getJobs();
                addNameTextbox.value = '';
            })
            .catch(error => console.error('Unable to add job.', error));
    }
    

    运行 UI 请求以添加作业,现在在没有触发器的情况下添加它(将在单独的部分中处理)。为了确认,然后我在浏览器中运行 API 请求以获取正在运行的调度程序的所有作业,使用: [https://localhost:44379/api/scheduler/QuartzScheduler/jobs] 结果:

    [{"name":"HelloWorldJob","group":"DEFAULT"},{"name":"TestJob","group":"DEFAULT"}]
    

    这意味着一些事情:

    1. 在正文中传递 JSON 对象不会将其与 API 函数参数相关联。我需要在 url 字符串中添加所有参数才能使用它们。可能有办法使用身体参数。
    2. 既然类在 API 中被正确引用,我可以继续只通过 UI 传递类名,而无需命名空间和程序集以确保其安全,因为类是在构建时在项目中定义的。
    3. 在 API 函数中添加 Console.Writeline 在运行时未返回任何输出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-04-21
      • 2013-12-10
      • 2021-09-20
      • 2017-03-15
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多