☰
learnyounode HTTP JSON API Server 练习:用原生 Node 实现 JSON 时间 API 服务
2026/9/29 7:08:02 网站建设 项目流程
  • 教程
  • CLI

【免费下载链接】learnyounode

Learn You The Node.js For Much Win! An intro to Node.js via a set of self-guided workshops.

项目地址:https://gitcode.com/gh_mirrors/le/learnyounode
点击查看免费下载

导读

本指南围绕 learnyounode 第 12 个练习 "HTTP JSON API Server"(对应任务文档 exercises/http_json_api_server/problem.vi.md)展开,目标是用 Node.js 核心模块编写一个返回 JSON 数据的 HTTP 服务器:通过url模块解析路径与查询字符串、用Date解析 ISO 格式时间、用JSON.stringify输出结构化响应。阅读本文后,你将掌握如何基于原生http+url模块完成多端点路由与 JSON 响应,并通过 learnyounode 的验证器(verifier)与官方测试用例检验代码的正确性。

练习目标:一个返回 JSON 的 HTTP 服务器

本练习要求编写一个名为http-json-api-server.js的文件,实现一个 HTTP服务器,在收到对特定路径的 GET 请求时返回 JSON 数据。服务器需要提供两个端点:

  1. /api/parsetime:请求携带查询字符串,其中 key 为iso,value 为 ISO 格式的时间字符串。例如:

    /api/parsetime?iso=2013-08-10T12:10:15.474Z

    响应 JSON 只包含三个属性:hour(时)、minute(分)、second(秒):

    { "hour": 14, "minute": 23, "second": 15 }
  2. /api/unixtime:接受相同的查询字符串,但返回 UNIX 纪元时间(自 1970-01-01 00:00:00 UTC 起的毫秒数),属性名为unixtime:

    { "unixtime": 1376136615474 }

服务器监听端口由程序第一个命令行参数指定。这与 learnyounode 之前的time_server练习一致——端口均由process.argv[2]注入,而非硬编码。

核心技术点拆解

原文档 problem.vi.md 的提示部分给出了四个关键线索,下面逐一结合源码展开。

1. 用request.url做端点路由

HTTP 服务器回调中的request对象带有url属性,它包含请求路径与查询字符串。要实现两个端点,就需要根据该属性"路由"请求。官方解答 exercises/http_json_api_server/solution/solution.js 用正则前缀匹配实现路由:

if (/^\/api\/parsetime/.test(req.url)) { result = parsetime(time) } else if (/^\/api\/unixtime/.test(req.url)) { result = unixtime(time) }

注意这里的^锚定只匹配路径开头,因此?iso=...查询串不会影响匹配结果。

2. 用new URL()解析 URL 与查询字符串

Node 核心模块url提供了 WHATWGURL类,new URL(request.url)能解析request.url并返回包含searchParams等有用属性的对象。原文档提示可以在命令行直接观察其结构:

$ node -pe "new URL('/test?q=1', 'http://example.com')"

一个重要的细节:new URL()解析相对路径(如/api/parsetime?iso=...)时需要第二个参数作为基础 URL(base),官方解答正是这样使用的:

const parsedUrl = new URL(req.url, 'http://example.com') const time = new Date(parsedUrl.searchParams.get('iso'))

URL#searchParams.get('iso')直接提取查询参数值,随后传入Date构造函数完成 ISO 字符串到时间对象的解析。更多细节可参考仓库内置文档 docs-nodejs/url.html。

3. 用Date完成 ISO 解析与时间换算

JavaScript 的Date对象既能以 ISO 格式输出时间(new Date().toISOString()),也能在构造函数中解析 ISO 字符串。官方解答将时间换算拆成两个纯函数:

function parsetime (time) { return { hour: time.getHours(), minute: time.getMinutes(), second: time.getSeconds() } } function unixtime (time) { return { unixtime: time.getTime() } }
  • Date#getHours()/getMinutes()/getSeconds():取出本地时区下的时分秒;
  • Date#getTime():返回自纪元起的毫秒数,直接构成unixtime值。

值得注意:getHours()系列方法返回的是服务器本地时区的时间。由于验证器生成的时间戳与实际比对逻辑(见下文)运行在同一主机上,时区影响会被抵消;但若部署到生产环境并希望统一按 UTC 输出,应改用getUTCHours()等 UTC 变体。

4. 以JSON.stringify输出并正确设置Content-Type

响应体必须是 JSON 字符串,使用JSON.stringify(result)序列化;同时作为"Web 世界的好公民",必须设置正确的Content-Type头:

res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify(result))

官方解答对无法路由的路径返回404:

if (result) { res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify(result)) } else { res.writeHead(404) res.end() }

完整官方解答示例

将上述要点组合起来,官方解答 exercises/http_json_api_server/solution/solution.js 全文如下(可直接作为可运行参考实现):

'use strict' const http = require('http') function parsetime (time) { return { hour: time.getHours(), minute: time.getMinutes(), second: time.getSeconds() } } function unixtime (time) { return { unixtime: time.getTime() } } const server = http.createServer(function (req, res) { const parsedUrl = new URL(req.url, 'http://example.com') const time = new Date(parsedUrl.searchParams.get('iso')) let result if (/^\/api\/parsetime/.test(req.url)) { result = parsetime(time) } else if (/^\/api\/unixtime/.test(req.url)) { result = unixtime(time) } if (result) { res.writeHead(200, { 'Content-Type': 'application/json' }) res.end(JSON.stringify(result)) } else { res.writeHead(404) res.end() } }) server.listen(Number(process.argv[2]))

验证器如何校验你的实现(源码级原理)

learnyounode 通过 exercises/http_json_api_server/exercise.js 自动验证提交。理解其机制有助于写出精确满足判定的代码:

  1. 随机端口:验证器调用 lib/rndport.js 生成端口(范围1024 + Math.floor(Math.random() * 64511)),提交程序与官方解答分别监听submissionPort与solutionPort,端口作为第一个参数传入子进程。
  2. 发起请求:query()函数在服务器启动约 500ms 后,用hyperquest以 GET 方式请求http://localhost:<port>/api/parsetime?iso=<ISO时间>和http://localhost:<port>/api/unixtime?iso=<ISO时间>,并用bl收集完整响应体。
  3. 归一化比较:normalizeJSON(data)先把响应JSON.parse再JSON.stringify,消除键顺序与空格差异,然后通过comparestdout与官方解答的输出逐字节比对。这意味着键名顺序、缩进风格都不影响判定,只要语义一致即可通过。
  4. 测试用例:仓库 test/http_json_api_server/ 中,valid_01.js是语义正确的实现,而invalid_02.js(unixtime返回getTime() + 1)、invalid_03.js(hour返回getHours() + 1)都会因输出与官方解答不一致而判定失败——这正说明验证器对两个端点返回值的精确性要求非常严格。

运行与验证命令

在学习环境中完成编码后,用验证命令检查正确性(文档 problem.md 中给出):

$ learnyounode verify http-json-api-server.js

本地手动联调时,可先启动服务器,再分别访问两个端点观察输出:

# 启动(监听 8000 端口) $ node http-json-api-server.js 8000 # 另开终端发起请求 $ curl "http://localhost:8000/api/parsetime?iso=2013-08-10T12:10:15.474Z" $ curl "http://localhost:8000/api/unixtime?iso=2013-08-10T12:10:15.474Z"

常见踩坑点与边界处理

结合源码与测试用例,以下细节最容易导致验证失败:

  • 端口必须取process.argv[2],并转为数字(官方解答用Number(process.argv[2]))。不监听该端口或硬编码端口都会导致连接失败。
  • 路由正则要锚定路径开头(/^\/api\/parsetime/),否则可能误匹配;也不要匹配到query部分。
  • 未匹配的路径需返回 404。虽然验证器只请求两个已知端点,但官方解答对null结果统一返回404,保持行为完整。
  • Content-Type必须为application/json,与文档要求一致。
  • 响应体必须是合法 JSON 字符串:JSON.stringify序列化后res.end输出;验证器的normalizeJSON会先JSON.parse,非法 JSON 会在 try/catch 中静默保留原文从而比对失败。

小结

本练习综合运用了 Node 原生http、url(WHATWGURL)与Date三大核心能力:new URL()负责解析路径与查询参数,Date负责 ISO 时间解析与毫秒换算,JSON.stringify与writeHead负责输出合规的 JSON 响应。通过 learnyounode 的自动验证器(随机端口、双请求、归一化比对)与 官方解答 的对照,你可以确认自己的实现与原文档要求完全一致——这套"路由 + 参数解析 + JSON 响应"的写法,也是日常 Node HTTP 服务开发中最基础、最常用的模式。

  • 教程
  • CLI

【免费下载链接】learnyounode

Learn You The Node.js For Much Win! An intro to Node.js via a set of self-guided workshops.

项目地址:https://gitcode.com/gh_mirrors/le/learnyounode
点击查看免费下载
上一篇:Hydra 版本升级完全指南:主版本、补丁版本与 Dev 版本的平滑迁移策略
下一篇:Linux 内核 Live Update Orchestrator(LUO)深度解析:基于 kexec 的在线内核升级与资源保留框架

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询