跳到正文

从 Node.js 到 Go Web

在 Express 和 Koa 中,路由回调接收 req、res 或 ctx,再自行读取参数。Memos 的 Go 服务方法却直接接收 Context 和生成的请求消息。要把熟悉的经验迁移过来,需要先看清这两层之间的 HTTP 路由、消息解码和协议适配。

这里选择开源笔记项目 Memos,源码固定在 b40d8a7b573774c52b022c8d6537d6baf2129dc0。这份提交的 origin 使用个人 fork,讲解链接指向固定提交,便于与后续上游变化对照。它使用 Go 1.27.2、标准库 net/http、Connect Go 1.20.0 和 gRPC-Gateway 2.30.0;服务接口从 Proto 生成,不以 Gin 或 Echo 作为路由基础。

读者可以默认熟悉 HTTP、JSON、Express / Koa 和数据库。Go 的方法接收者、指针、接口、goroutine 和 Context 只在影响阅读的位置解释。这组文章围绕后端请求链路,不要求先掌握全部 Go 语法,也不展开数据库实现或前端业务。

由于篇幅问题,全文分成 6 部分来讲。

第一篇《应用入口与 HTTP 处理》从启动、监听和 Handler 接口开始,解释标准库如何接入请求,项目如何组合路由与中间件,以及共享服务对象为什么不能存放当前请求的数据。

第二篇《从 Proto 找到路由与参数》追踪 REST 与 Connect 两种入口,解释请求体层级、路径和查询参数如何进入消息,再用生成代码和手写包装器把服务函数的调用方找出来。

第三篇《请求消息与响应转换》区分 API 消息、Store 对象和公开响应。类型、缺省值、业务校验、JSON 配置及 FieldMask 都放在各自的边界中说明,避免把 Proto 契约误当成完整业务校验。

第四篇《对象装配、Context 与登录用户》分别追踪启动时的共享依赖与请求时的用户身份。通过 Authorizer、两种传输入口和 fetchCurrentUser,解释 Go Context 与 Koa ctx 能类比到哪里,以及取消信号如何向下传递。

第五篇《沿着创建 Memo 读完整请求》串起身份检查、输入准备、Store 变更、响应转换和通知。首次写入前后可能出现的不同失败,以及它们对重试的影响,是完整链路中需要特别辨认的部分。

第六篇《用测试区分业务与传输边界》对照直接服务测试、httptest 与错误适配测试,解释各自覆盖什么、绕过什么,并保留针对性的运行命令和核对范围。

同一组问题还可以对照FastAPI 系列与Spring MVC 系列。FastAPI 主要通过函数声明与依赖解析准备值,Memos 通过 Proto 和适配器把值显式传入,Spring 则要同时观察 Bean 装配和 MVC 参数解析。具体差异以各自项目实际实现为准。