跳至页脚内容
.NET 帮助

Hangfire .NET Core(开发者如何使用)

现代应用程序开发通常需要处理后台任务以处理巨大的工作负载。 在这种情况下,我们需要能够执行多个作业的后台作业处理程序。 对于C# .NET Core应用程序,其中一个后台作业处理程序是Hangfire。在这篇博客中,我们将学习管理Hangfire后台作业以及如何将其与其他软件包(如用于PDF生成的IronPDF)一起使用以在后台生成PDF文档。

Hangfire 通过提供一个可靠且灵活的框架来简化ASP.NET Core或.NET Core 6 Web API应用程序中的后台处理,实现对后台作业的管理和执行。 Hangfire作为NuGet包提供,可以按照以下说明使用.NET CLI安装:

dotnet add package Hangfire --version 1.8.6

.NET Core Web API中的实现

要了解Hangfire,让我们创建一个简单的.NET Core API应用程序,并使用CLI安装Hangfire。

dotnet new webapi -n HangfireDemo
cd HangfireDemo
dotnet build
dotnet add package Hangfire --version 1.8.6
dotnet build
dotnet new webapi -n HangfireDemo
cd HangfireDemo
dotnet build
dotnet add package Hangfire --version 1.8.6
dotnet build
SHELL

这里我们使用.NET CLI创建一个简单的天气REST API。 第一行创建一个名为HangfireDemo的.NET Core Web API项目,以执行API端点。 第二行导航到我们新创建的文件夹“HangfireDemo”,然后构建项目。 接下来,我们将Hangfire NuGet包添加到项目中并再次构建。 之后,你可以在任意选择的编辑器中打开你的项目,例如Visual Studio 2022或JetBrains Rider。 现在,如果运行项目,可以看到如下Swagger所示:

Hangfire .NET Core(开发者如何使用):图1 - Swagger

在这里我们可以看到天气GET API,它返回日期,概要和温度。

Hangfire .NET Core(开发者如何使用):图2 - 天气GET API

现在让我们添加一个Hangfire后台作业处理器。 在Visual Studio中打开项目。

添加Hangfire作业处理器

在应用程序中配置Hangfire,通常在Startup.cs文件中。 这涉及设置作业存储和初始化Hangfire服务器。

// Startup.cs
using Hangfire;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add Hangfire services and use SQL Server as storage option
        services.AddHangfire(config => config.UseSqlServerStorage("your_connection_string"));
        services.AddHangfireServer();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // Use Hangfire Server and Dashboard for monitoring and managing jobs
        app.UseHangfireServer();
        app.UseHangfireDashboard();
        // Your other configuration settings
    }
}
// Startup.cs
using Hangfire;

public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add Hangfire services and use SQL Server as storage option
        services.AddHangfire(config => config.UseSqlServerStorage("your_connection_string"));
        services.AddHangfireServer();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        // Use Hangfire Server and Dashboard for monitoring and managing jobs
        app.UseHangfireServer();
        app.UseHangfireDashboard();
        // Your other configuration settings
    }
}
' Startup.cs
Imports Hangfire

Public Class Startup
	Public Sub ConfigureServices(ByVal services As IServiceCollection)
		' Add Hangfire services and use SQL Server as storage option
		services.AddHangfire(Function(config) config.UseSqlServerStorage("your_connection_string"))
		services.AddHangfireServer()
	End Sub

	Public Sub Configure(ByVal app As IApplicationBuilder, ByVal env As IHostingEnvironment)
		' Use Hangfire Server and Dashboard for monitoring and managing jobs
		app.UseHangfireServer()
		app.UseHangfireDashboard()
		' Your other configuration settings
	End Sub
End Class
$vbLabelText   $csharpLabel

ConfigureServices方法用于添加存储,以保存Hangfire新创建的作业。 在这里,使用了SQL Server数据库。 SQL Server连接字符串应替换为"your_connection_string"。 还可以使用Hangfire.InMemory进行内存存储。

dotnet add package Hangfire.InMemory --version 0.6.0

并替换为:

services.AddHangfire(configuration => { configuration.UseInMemoryStorage(); });
services.AddHangfire(configuration => { configuration.UseInMemoryStorage(); });
services.AddHangfire(Sub(configuration)
	configuration.UseInMemoryStorage()
End Sub)
$vbLabelText   $csharpLabel

创建后台作业

定义要作为后台作业运行的方法。 这些方法应为无参数构造函数的类的静态或实例方法。 作业可以作为重复作业运行,也可以同时运行多个作业。

public class MyBackgroundJob
{
    public void ProcessJob()
    {
        // Background job logic, can be a recurring job or multiple jobs
        Console.WriteLine("Background job is running...");
    }
}
public class MyBackgroundJob
{
    public void ProcessJob()
    {
        // Background job logic, can be a recurring job or multiple jobs
        Console.WriteLine("Background job is running...");
    }
}
Public Class MyBackgroundJob
	Public Sub ProcessJob()
		' Background job logic, can be a recurring job or multiple jobs
		Console.WriteLine("Background job is running...")
	End Sub
End Class
$vbLabelText   $csharpLabel

入队作业

使用Hangfire API入队后台作业。 您可以安排后台作业在特定时间、延迟后或定期运行。

// Enqueue a job to run immediately
BackgroundJob.Enqueue<MyBackgroundJob>(x => x.ProcessJob());

// Schedule a job to run after a 5-minute delay
BackgroundJob.Schedule<MyBackgroundJob>(x => x.ProcessJob(), TimeSpan.FromMinutes(5));

// Schedule a recurring job using a job ID
RecurringJob.AddOrUpdate<MyBackgroundJob>("jobId", x => x.ProcessJob(), Cron.Daily);
// Enqueue a job to run immediately
BackgroundJob.Enqueue<MyBackgroundJob>(x => x.ProcessJob());

// Schedule a job to run after a 5-minute delay
BackgroundJob.Schedule<MyBackgroundJob>(x => x.ProcessJob(), TimeSpan.FromMinutes(5));

// Schedule a recurring job using a job ID
RecurringJob.AddOrUpdate<MyBackgroundJob>("jobId", x => x.ProcessJob(), Cron.Daily);
' Enqueue a job to run immediately
BackgroundJob.Enqueue(Of MyBackgroundJob)(Function(x) x.ProcessJob())

' Schedule a job to run after a 5-minute delay
BackgroundJob.Schedule(Of MyBackgroundJob)(Function(x) x.ProcessJob(), TimeSpan.FromMinutes(5))

' Schedule a recurring job using a job ID
RecurringJob.AddOrUpdate(Of MyBackgroundJob)("jobId", Function(x) x.ProcessJob(), Cron.Daily)
$vbLabelText   $csharpLabel

Hangfire仪表板和服务器

Hangfire仪表板和服务器可以在Configure方法中添加以进行实时作业监控。

// Run Hangfire server and dashboard
app.UseHangfireServer();
app.UseHangfireDashboard();
// Run Hangfire server and dashboard
app.UseHangfireServer();
app.UseHangfireDashboard();
' Run Hangfire server and dashboard
app.UseHangfireServer()
app.UseHangfireDashboard()
$vbLabelText   $csharpLabel

服务器也可以在ConfigureServices中添加。

services.AddHangfireServer();
services.AddHangfireServer();
services.AddHangfireServer()
$vbLabelText   $csharpLabel

即时火箭作业

// Fire and forget jobs are executed only once and almost immediately after creation.
var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!")); // Job ID for fire and forget job
// Fire and forget jobs are executed only once and almost immediately after creation.
var jobId = BackgroundJob.Enqueue(() => Console.WriteLine("Fire-and-forget!")); // Job ID for fire and forget job
' Fire and forget jobs are executed only once and almost immediately after creation.
Dim jobId = BackgroundJob.Enqueue(Sub() Console.WriteLine("Fire-and-forget!")) ' Job ID for fire and forget job
$vbLabelText   $csharpLabel

重复作业

// Recurring jobs fire many times based on a specified CRON schedule.
RecurringJob.AddOrUpdate("myrecurringjob", () => Console.WriteLine("Recurring!"), Cron.Daily);
// Recurring jobs fire many times based on a specified CRON schedule.
RecurringJob.AddOrUpdate("myrecurringjob", () => Console.WriteLine("Recurring!"), Cron.Daily);
' Recurring jobs fire many times based on a specified CRON schedule.
RecurringJob.AddOrUpdate("myrecurringjob", Sub() Console.WriteLine("Recurring!"), Cron.Daily)
$vbLabelText   $csharpLabel

延迟作业

// Delayed jobs are executed only once but after a specified interval.
var jobId = BackgroundJob.Schedule(() => Console.WriteLine("Delayed!"), TimeSpan.FromDays(7));
// Delayed jobs are executed only once but after a specified interval.
var jobId = BackgroundJob.Schedule(() => Console.WriteLine("Delayed!"), TimeSpan.FromDays(7));
' Delayed jobs are executed only once but after a specified interval.
Dim jobId = BackgroundJob.Schedule(Sub() Console.WriteLine("Delayed!"), TimeSpan.FromDays(7))
$vbLabelText   $csharpLabel

续作

// Continuation jobs are executed once their parent jobs have completed.
BackgroundJob.ContinueJobWith(jobId, () => Console.WriteLine("Continuation!"));
// Continuation jobs are executed once their parent jobs have completed.
BackgroundJob.ContinueJobWith(jobId, () => Console.WriteLine("Continuation!"));
' Continuation jobs are executed once their parent jobs have completed.
BackgroundJob.ContinueJobWith(jobId, Sub() Console.WriteLine("Continuation!"))
$vbLabelText   $csharpLabel

批量作业

// Batch is a group of background jobs created atomically and considered as a single entity.
var batchId = BatchJob.StartNew(x =>
{
    x.Enqueue(() => Console.WriteLine("Job 1"));
    x.Enqueue(() => Console.WriteLine("Job 2"));
});
// Batch is a group of background jobs created atomically and considered as a single entity.
var batchId = BatchJob.StartNew(x =>
{
    x.Enqueue(() => Console.WriteLine("Job 1"));
    x.Enqueue(() => Console.WriteLine("Job 2"));
});
' Batch is a group of background jobs created atomically and considered as a single entity.
Dim batchId = BatchJob.StartNew(Sub(x)
	x.Enqueue(Sub() Console.WriteLine("Job 1"))
	x.Enqueue(Sub() Console.WriteLine("Job 2"))
End Sub)
$vbLabelText   $csharpLabel

批量续作

// Batch continuation is fired when all background jobs in a parent batch are finished.
BatchJob.ContinueBatchWith(batchId, x =>
{
    x.Enqueue(() => Console.WriteLine("Last Job"));
});
// Batch continuation is fired when all background jobs in a parent batch are finished.
BatchJob.ContinueBatchWith(batchId, x =>
{
    x.Enqueue(() => Console.WriteLine("Last Job"));
});
' Batch continuation is fired when all background jobs in a parent batch are finished.
BatchJob.ContinueBatchWith(batchId, Sub(x)
	x.Enqueue(Sub() Console.WriteLine("Last Job"))
End Sub)
$vbLabelText   $csharpLabel

仪表板

Hangfire仪表板是您可以查找所有关于后台作业信息的地方。 它被编写为一个OWIN中间件(如果您不熟悉OWIN,不用担心),因此您可以将其插件到您的ASP.NET、ASP.NET MVC、Nancy和ServiceStack应用程序中,同时使用OWIN自托管功能在控制台应用程序或Windows服务中托管Dashboard。

当启用仪表板时,它在/hangfire/扩展下可用。 在这个仪表板中,您可以管理正在运行的后台作业、安排后台作业,并查看一次性作业以及重复作业。 可以使用作业ID标识作业。

实时处理

Hangfire .NET Core(开发者如何使用):图3 - 作业实时处理

成功作业

查看下面的成功作业。

Hangfire .NET Core(开发者如何使用):图4 - 成功作业

已调度的作业

Hangfire .NET Core(开发者如何使用):图5 - 已调度作业

当您的应用程序运行时,Hangfire将根据配置设置处理后台作业。

Remember to check the Hangfire documentation for more advanced configuration options and features: Hangfire Documentation and complete code can be found on GitHub Hangfire Demo.

Introducing IronPDF

IronPDF for .NET PDF Generation is a NuGet package from NuGet软件包,有助于读取和生成PDF文档。 它可以轻松将格式化的文档带有样式信息转换为PDF。 IronPDF 可以轻松从HTML内容生成PDF。 它可以从URL下载HTML并生成PDF。

IronPDF的主要吸引力是其HTML到PDF转换功能,保持布局和样式。 它可以从网页内容创建PDF,非常适合报告、发票和文档。 C# Double Question Mark (开发人员如何工作): 图1 - IronPDF网页

using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
using IronPdf;

class Program
{
    static void Main(string[] args)
    {
        var renderer = new ChromePdfRenderer();

        // Convert HTML String to PDF
        var htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>";
        var pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent);
        pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf");

        // Convert HTML File to PDF
        var htmlFilePath = "path_to_your_html_file.html"; // Specify the path to your HTML file
        var pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath);
        pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf");

        // Convert URL to PDF
        var url = "http://ironpdf.com"; // Specify the URL
        var pdfFromUrl = renderer.RenderUrlAsPdf(url);
        pdfFromUrl.SaveAs("URLToPDF.pdf");
    }
}
Imports IronPdf

Friend Class Program
	Shared Sub Main(ByVal args() As String)
		Dim renderer = New ChromePdfRenderer()

		' Convert HTML String to PDF
		Dim htmlContent = "<h1>Hello, IronPDF!</h1><p>This is a PDF from an HTML string.</p>"
		Dim pdfFromHtmlString = renderer.RenderHtmlAsPdf(htmlContent)
		pdfFromHtmlString.SaveAs("HTMLStringToPDF.pdf")

		' Convert HTML File to PDF
		Dim htmlFilePath = "path_to_your_html_file.html" ' Specify the path to your HTML file
		Dim pdfFromHtmlFile = renderer.RenderHtmlFileAsPdf(htmlFilePath)
		pdfFromHtmlFile.SaveAs("HTMLFileToPDF.pdf")

		' Convert URL to PDF
		Dim url = "http://ironpdf.com" ' Specify the URL
		Dim pdfFromUrl = renderer.RenderUrlAsPdf(url)
		pdfFromUrl.SaveAs("URLToPDF.pdf")
	End Sub
End Class
$vbLabelText   $csharpLabel

开始使用 IronPDF

安装 IronPDF 库

使用 NuGet 包管理器安装

要将IronPDF集成到您的Hangfire .NET项目中以使用NuGet包管理器,请按照以下步骤进行:

  1. 打开Visual Studio,右键单击解决方案资源管理器中的项目。
  2. 从上下文菜单中选择“管理NuGet软件包...”。
  3. 转到浏览选项卡并搜索IronPDF。
  4. 从搜索结果中选择IronPDF库并点击安装按钮。
  5. 接受任何许可协议提示。

如果你喜欢使用包管理器控制台,请执行以下命令:

Install-Package IronPdf

这将获取并将IronPDF安装到项目中。

使用 NuGet 网站安装

有关IronPDF的详细概述,包括功能、兼容性和额外下载选项,请访问NuGet网站上的IronPDF页面 https://www.nuget.org/packages/IronPdf

通过 DLL 安装

或者,您可以使用其 DLL 文件将 IronPDF 直接集成到项目中。从此 IronPDF 直接下载 下载包含 DLL 的 ZIP 文件。 解压缩它,并将 DLL 纳入您的项目。

现在,让我们修改我们的应用程序以添加一个后台处理作业来以PDF格式下载网页。

namespace HangfireDemo.Core;

public class PdfGenerationJob
{
    public void Start(string website)
    {
        // Create a PDF from any existing web page
        ChromePdfRenderer renderer = new ChromePdfRenderer();
        PdfDocument pdf = renderer.RenderUrlAsPdf(website);
        var filePath = AppContext.BaseDirectory + "result.pdf";
        pdf.SaveAs(filePath);
    }
}
namespace HangfireDemo.Core;

public class PdfGenerationJob
{
    public void Start(string website)
    {
        // Create a PDF from any existing web page
        ChromePdfRenderer renderer = new ChromePdfRenderer();
        PdfDocument pdf = renderer.RenderUrlAsPdf(website);
        var filePath = AppContext.BaseDirectory + "result.pdf";
        pdf.SaveAs(filePath);
    }
}
Namespace HangfireDemo.Core

	Public Class PdfGenerationJob
		Public Sub Start(ByVal website As String)
			' Create a PDF from any existing web page
			Dim renderer As New ChromePdfRenderer()
			Dim pdf As PdfDocument = renderer.RenderUrlAsPdf(website)
			Dim filePath = AppContext.BaseDirectory & "result.pdf"
			pdf.SaveAs(filePath)
		End Sub
	End Class
End Namespace
$vbLabelText   $csharpLabel

IronPDF具有内置方法,可从URL下载网站并将其保存为PDF文档。 我们将在作业中使用此方法下载并保存到指定位置。 此后台作业可以修改以获取多个网站URL并将其保存为PDF。

现在,让我们添加一个控制器以公开PDF生成和下载API。

using Hangfire;
using HangfireDemo.Core;
using Microsoft.AspNetCore.Mvc;

namespace HangfireDemo.Controllers;

[ApiController]
[Route("[controller]")]
public class PdfGeneratorController : ControllerBase
{
    [HttpGet("request", Name = "Start PDF Generation")]
    public void Start([FromQuery] string websiteUrl)
    {
        BackgroundJob.Enqueue<PdfGenerationJob>(x => x.Start(websiteUrl));
    }

    [HttpGet("result", Name = "Download PDF Generation")]
    public IActionResult WebResult()
    {
        var filePath = AppContext.BaseDirectory + "result.pdf";
        var stream = new FileStream(filePath, FileMode.Open);
        return new FileStreamResult(stream, "application/octet-stream") { FileDownloadName = "website.pdf" };
    }
}
using Hangfire;
using HangfireDemo.Core;
using Microsoft.AspNetCore.Mvc;

namespace HangfireDemo.Controllers;

[ApiController]
[Route("[controller]")]
public class PdfGeneratorController : ControllerBase
{
    [HttpGet("request", Name = "Start PDF Generation")]
    public void Start([FromQuery] string websiteUrl)
    {
        BackgroundJob.Enqueue<PdfGenerationJob>(x => x.Start(websiteUrl));
    }

    [HttpGet("result", Name = "Download PDF Generation")]
    public IActionResult WebResult()
    {
        var filePath = AppContext.BaseDirectory + "result.pdf";
        var stream = new FileStream(filePath, FileMode.Open);
        return new FileStreamResult(stream, "application/octet-stream") { FileDownloadName = "website.pdf" };
    }
}
Imports Hangfire
Imports HangfireDemo.Core
Imports Microsoft.AspNetCore.Mvc

Namespace HangfireDemo.Controllers

	<ApiController>
	<Route("[controller]")>
	Public Class PdfGeneratorController
		Inherits ControllerBase

		<HttpGet("request", Name := "Start PDF Generation")>
		Public Sub Start(<FromQuery> ByVal websiteUrl As String)
			BackgroundJob.Enqueue(Of PdfGenerationJob)(Function(x) x.Start(websiteUrl))
		End Sub

		<HttpGet("result", Name := "Download PDF Generation")>
		Public Function WebResult() As IActionResult
			Dim filePath = AppContext.BaseDirectory & "result.pdf"
			Dim stream = New FileStream(filePath, FileMode.Open)
			Return New FileStreamResult(stream, "application/octet-stream") With {.FileDownloadName = "website.pdf"}
		End Function
	End Class
End Namespace
$vbLabelText   $csharpLabel

在这里我们创建了两个API:一个用于启动后台作业并获取用于启动下载的网站URL,另一个API用于下载生成的PDF。 API显示如下。

Hangfire .NET Core(开发者如何使用):图7 - PDF生成API

结果如下所示:

Hangfire .NET Core(开发者如何使用):图8 - 输出

许可(提供免费试用)

为了使上述代码在没有水印的情况下工作,需要许可证密钥。 开发人员可以在注册IronPDF免费试用时获得试用许可证。 试用许可证不需要信用卡。 您可以提供您的电子邮件ID并注册免费试用。

结论

Hangfire和IronPDF是用于在后台生成和下载PDF的绝佳组合。 Hangfire可以有效处理长时间的任务,而IronPDF提供了灵活且易于使用的PDF生成解决方案。 要了解更多关于IronPDF的信息,可以访问IronPDF文档

另外,探索来自Iron Software产品套件的其他工具,这些工具可以提升您的编码技能并满足现代应用程序需求。

常见问题解答

.NET Core 中的 Hangfire 是什么?

Hangfire 是一个框架,可以简化在 ASP.NET Core 或 .NET Core 6 应用程序中实现后台处理。它为管理和执行后台作业提供了可靠且灵活的解决方案。

如何在 .NET Core 应用程序中安装 Hangfire?

Hangfire 可以作为 NuGet 包安装。您可以使用 .NET CLI 添加它,命令如下:dotnet add package Hangfire --version 1.8.6

Hangfire 支持哪几种类型的后台作业?

Hangfire 支持多种类型的后台作业,包括即火即忘作业、延迟作业、周期性作业和持续作业。

如何在 .NET Core 应用程序中配置 Hangfire?

Hangfire 在 Startup.cs 文件中配置,您可以在此处设置作业存储并初始化 Hangfire 服务器。通常需要添加 Hangfire 服务并设置 SQL Server 或内存存储。

什么是 Hangfire Dashboard?

Hangfire Dashboard 是一个用于监控和管理后台作业的工具。它提供有关实时处理、成功作业和计划作业的信息,并通过网页界面访问。

如何使用 Hangfire 创建后台作业?

可以通过定义您想要作为作业运行的方法并使用 Hangfire API 将其排队来创建后台作业。作业可以安排立即运行,延迟后运行或定期运行。

如何在 .NET Core中执行后台的 PDF 生成任务?

您可以使用支持 HTML 到 PDF 转换的 PDF 库来在后台执行 PDF 生成任务。这可以集成到像 Hangfire 这样的后台作业处理框架中,以便自动将 HTML 内容创建为 PDF。

PDF 生成库在 .NET 中的一些功能是什么?

PDF 生成库可以将 HTML 字符串、HTML 文件和 URL 转换为 PDF。它保留布局和样式,对于从网页内容生成报告、发票和文档非常有用。

如何在 .NET 项目中安装 PDF 生成库?

可以使用 Visual Studio 中的 NuGet 包管理器或通过包管理器控制台使用特定命令来安装 PDF 生成库。还可以通过从库网站直接下载 DLL 进行安装。

使用没有水印的 PDF 生成库需要什么条件?

使用没有水印的 PDF 生成库通常需要许可证密钥。可能需要通过在库网站上注册来获取免费试用许可证。

如何在 .NET Core 中将 PDF 生成工具与 Hangfire 集成?

您可以通过设置一个使用 PDF 生成库将 HTML 转换为 PDF 的后台作业,在 .NET Core 中将 PDF 生成工具与 Hangfire 集成。这允许在应用程序中实现自动文档创建和管理。

Curtis Chau
技术作家

Curtis Chau 拥有卡尔顿大学的计算机科学学士学位,专注于前端开发,精通 Node.js、TypeScript、JavaScript 和 React。他热衷于打造直观且美观的用户界面,喜欢使用现代框架并创建结构良好、视觉吸引力强的手册。

除了开发之外,Curtis 对物联网 (IoT) 有浓厚的兴趣,探索将硬件和软件集成的新方法。在空闲时间,他喜欢玩游戏和构建 Discord 机器人,将他对技术的热爱与创造力相结合。