Cloudflare教程(二)- Wokers 上一篇博客Cloudflare教程(一)-Wokers
需求背景 接着整我自己的ChinaHolidayAPI接口。
技术选型
Cloudflare的Wokers
ChinaHolidayAPI
D1 SQL数据库 – 这相当于MySQL
Workers KV数据库 – 这相当于redis
R2对象存储 – 这相当于OSS,我准备拿来存节假日的json文件
搭建过程 创建Wokers工程 创建一个GitHub的私有仓库仓库,用来存储我们的Wokers代码。当然这个过程你也可以没有,每次手动到cloudflare的网页端去更新文件也可以。不过我为了后续有可能增加其他功能,能持续的更新,还是准备了一个仓库。
参考cloudflare的Workers文档 安装nodejs,安装命令行工具,在我们的仓库目录中新建一个workers项目。
注意cloudflare要求的node版本 ,我使用的是v22.12.0
1 npm create cloudflare@latest -- wooapi
我自己是准备后续做一个api的集合的,所以起了个名字:wooapi
创建的过程中,咱们就使用最简单的模板,从头开始写,依次选择:
Hello World example
Worker only
JavaScript
等待npm安装完依赖,工具会提示你是否关联当前git仓库用于版本管理。我选择了yes,本来我也就打算之后用这个仓库关联到cloudflare,到时候自动更新部署。
接着会提示:是否现在部署?我选择现在部署。可以先直接看看效果。如果你没有使用过工具,会弹出网页让你登录你的cloudflare账号。登录之后,工具会继续执行部署操作。
等待部署完成之后,你可以看到workers的域名。可以直接访问看看。页面会直接返回:Hello World!
当然你也可以去cloudflare后台查看该workers的配置,同时添加自定义域名。我这里直接就添加了自定义域名,后续测试都使用该正式域名了。
wokers的部署完成之后,我们可以开始编写代码了。先看看cloudflare帮我们生成的项目结构:
项目结构里面,我们需要关心的只有两个地方: src/index.js – 这是我们的代码 wrangler.jsonc – 这是wrokers的配置文件,包括绑定的数据库参数,项目入口文件等都在这里
我们修改一下index.js的内容,命令行进入wooapi目录,执行命令:
工具输出:[wrangler:info] Ready on http://localhost:8787
此时,我们的workers已经在本地启动,访问http://localhost:8787便能看到我们修改的页面了。
我将代码改为:
1 2 3 4 5 export default { async fetch (request, env, ctx ) { return new Response ('Hello WorldAAA!' ); }, };
可以看到工具输出:⎔ Reloading local server… index.js被重新加载了。此时我们访问页面,看到的就是:Hello WorldAAA!
实现从D1数据库读取数据 查看文档:https://developers.cloudflare.com/workers/runtime-apis/bindings/ 可以看到workers支持非常多的资源,都可以绑定到workers上。
如何绑定D1数据库 查看文档:https://developers.cloudflare.com/d1/get-started/#3-bind-your-worker-to-your-d1-database 文档说的非常清楚,你可以使用wrangler d1 create 命令直接创建一个D1数据库,同时该命令也会将该数据库绑定到workers中(wrangler.jsonc自动添加D1数据库的配置信息)。 也可以手动添加配置,配置一个已经存在的D1数据库。由于我们之前已经收集过节假日信息,创建过一个D1数据库,所以这里我们选择手动添加配置。
1 2 3 4 5 6 7 8 9 { "d1_databases" : [ { "binding" : "你希望在代码里使用的变量名,这个必须符合js变量命名规范" , "database_name" : "你的数据库名称" , "database_id" : "你的数据库ID" } ] }
1 2 3 4 5 6 7 8 9 export default { async fetch (request, env, ctx ) { const { results } = await env.n8n .prepare ("SELECT * FROM holidays limit ?;" ) .bind ("10" ) .run (); return Response .json (results); }, };
当你配置好之后,并且参考文档修改了代码,从D1读取数据,此时访问本地的服务,你会发现居然报错了。 我的第一反应是,代码写的有问题?然后检查代码,并且把SQL拿到D1后台去执行,发现是能正常查询到数据的。我便怀疑是不是本地启动服务,查询不了数据库?那我部署到线上试试看。
执行命令,进行部署。
等待部署完成。访问正式地址。nice!数据正常返回了,我们已经成功的将D1绑定到workers,并且在其中实现读取数据了。
实现根据规则查询数据 确定需求 首先要确定的是我们的需求,追求简单,我们就按照原仓库的接口来实现。我们仅支持GET请求。 此接口定义来源于开源仓库:https://github.com/Dreace/ChinaHolidayAPI
域名统一为:https://wooapi.664663.xyz/holidays – 出于扩展考虑,/holidays 为查询节假日接口
|接口样式|含义|备注| |无参数|查询当日类型|| |?date=2025-09-09|查询指定日期类型||
实现代码 我们写一段测试代码,用来获取我们需要的参数。这段代码不涉及D1的查询,我们直接在本地就能测试。
1 2 3 4 5 6 7 8 9 10 export default { async fetch (request, env, ctx ) { const { pathname, searchParams} = new URL (request.url ); return new Response (pathname+" " +searchParams.get ("date" ) +" " + request.method ) }, };
我们访问 http://localhost:8787/holidays?date=2025-09-09 可以看到接口返回的是:/holidays 2025-09-09 GET
那么我们就可以开始实现我们的代码了:
先判断是否**/holidays**,是的话就是查询节假日的请求;
再判断是否GET 请求,是则继续执行,否则返回错误;
最后获取date参数,有效则查询指定日期,无效则查询当日类型;
根据查询的数据进行判断返回。
流程很简单,代码也不长,我也没有去优化。以下就是全部代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 export default { async fetch (request, env, ctx ) { const { pathname, searchParams} = new URL (request.url ); if (pathname === '/holidays' ){ if (request.method !== 'GET' ){ return new Response ( JSON .stringify ({ error : "仅支持GET请求" }), { headers : { "Content-Type" : "application/json" }, status : 405 } ); } var dateParam = searchParams.get ('date' ); if (!dateParam) { dateParam = this .getShanghaiDate () } const dateRegex = /^\d{4}-\d{2}-\d{2}$/ ; if (!dateRegex.test (dateParam)) { return new Response ( JSON .stringify ({ error : "无效格式,正确格式:YYYY-MM-DD" }), { headers : { "Content-Type" : "application/json" }, status : 400 } ); } const [year, month, day] = dateParam.split ('-' ).map (Number ); const date = new Date (year, month - 1 , day); if ( date.getFullYear () !== year || date.getMonth () + 1 !== month || date.getDate () !== day ) { return new Response ( JSON .stringify ({ error : "日期不存在(如2025-02-30)" }), { headers : { "Content-Type" : "application/json" }, status : 400 } ); } var result = {date :dateParam,isHoliday :false ,note :"普通工作日" ,type :"工作日" } const { results } = await env.n8n .prepare ("SELECT * FROM holidays WHERE date = ?;" ) .bind (dateParam) .run (); if (Array .isArray (results) && results.length === 0 ) { if (this .isWeekend (dateParam)){ result = {date :dateParam,isHoliday :true ,note :"周末" ,type :"假日" } }else { result = {date :dateParam,isHoliday :false ,note :"普通工作日" ,type :"工作日" } } return Response .json (result); } const resultFirst = results[0 ] if (resultFirst.type === "补班日" ){ result = {date :dateParam,isHoliday :true ,note :"补班日" ,type :"工作日" } }else { result = {date :dateParam,isHoliday :true ,note :resultFirst.note ,type :"假日" } } return Response .json (result); } return new Response ("OK" ); }, getShanghaiDate ( ) { const now = new Date (); const utcOffset = 8 * 60 * 60 * 1000 ; const shanghaiTime = new Date (now.getTime () + utcOffset); const year = shanghaiTime.getUTCFullYear (); const month = String (shanghaiTime.getUTCMonth () + 1 ).padStart (2 , '0' ); const day = String (shanghaiTime.getUTCDate ()).padStart (2 , '0' ); return `${year} -${month} -${day} ` ; }, isWeekend (dateStr ) { const [year, month, day] = dateStr.split ('-' ).map (Number ); const date = new Date (year, month - 1 , day); const dayOfWeek = date.getDay (); return dayOfWeek === 0 || dayOfWeek === 6 ; }, };
总结 至此,节假日查询接口就完成了,代码我没有特意去优化,现有的代码肯定是不能满足线上使用的需求的,比如缓存没有做,现在都是查询D1数据,效率不高。可以考虑从KV查询数据,并且将工作日的数据也存入KV,避免每次查询工作日,都请求到D1去。 还有诸如请求频率限制等等的问题,这里就不一一列举了。
后续我还会继续完善holidays接口,实现缓存,限制频率等。现阶段它还不能作为正式使用的接口,请勿使用。
测试地址:https://wooapi.664663.xyz/holidays?date=2025-05-05
同时,现在也还没有将GitHub仓库和cloudflare workers关联起来,所以代码修改后,需要手动执行npx wrangler deploy 命令部署。后续我会一一实现这些功能。