Learn HTTP Servers in Go(Boot.dev)

Boot.dev 的 Learn HTTP Servers in Go 课程框架笔记——用 net/http 搭建服务端、路由、中间件与可测试服务,依赖 HTTP 协议与客户端前置。

#type / resource #status / growing #society / education #resource / boot-dev #resource / go #media / course

[!info] 关联笔记

Learn HTTP Servers in Go:课程知识整理

[!info] 课程概况 截至 2026 年 8 月 3 日,这门课程公开页面显示约 24 小时、51 节课、9 个章节。课程不依赖 Web 框架,主要使用 Go 标准库 net/http,逐步完成一个类似 Twitter 的微型社交平台后端 Chirpy


一、课程最终要完成什么

整个课程围绕一条完整的后端请求链展开:

客户端
  ↓ HTTP 请求
ServeMux 路由

中间件 Middleware

Handler 处理器

JSON 解析与参数校验

身份认证与权限检查

PostgreSQL 数据库

JSON HTTP 响应

最终项目大致包含:

GET    /healthz
GET    /admin/metrics
POST   /admin/reset

POST   /api/users
PUT    /api/users
POST   /api/login

POST   /api/chirps
GET    /api/chirps
GET    /api/chirps/{chirpID}
DELETE /api/chirps/{chirpID}

POST   /api/refresh
POST   /api/revoke

POST   /api/polka/webhooks

同时实现:

  • JSON REST API
  • PostgreSQL 数据持久化
  • 数据库迁移
  • 类型安全 SQL
  • 密码哈希
  • JWT Access Token
  • Refresh Token
  • 用户资源权限检查
  • Webhook
  • API Key
  • 查询参数与排序
  • API 文档和 README

这些目标与课程公开介绍一致。


二、完整课程目录

Chapter 1:Servers

共 10 节:

  1. Welcome to Learn HTTP Servers
  2. Goroutines in Servers
  3. Project Setup
  4. Server
  5. Fileservers
  6. Fileserver Quiz
  7. Serving Images
  8. Workflow Tips
  9. Custom Handlers
  10. Handler Review

主要讲解服务器、并发请求、http.Server、静态文件服务和 Handler 模型。

Chapter 2:Routing

共 4 节:

  1. Middleware
  2. Stateful Handlers
  3. Routing
  4. Patterns

主要讲中间件、带状态的 Handler、HTTP 方法路由和 Go 1.22 的路由模式。

Chapter 3:Architecture

共 4 节:

  1. Monoliths and Decoupling
  2. Which Is Better?
  3. Admin Namespace
  4. Deployment Options

主要讲单体架构、前后端分离、接口命名空间和部署方式。

Chapter 4:JSON

共 4 节:

  1. HTTP Clients
  2. JSON
  3. JSON Review
  4. The Profane

主要讲使用 HTTP 客户端调试、解析请求 JSON、返回 JSON、输入校验和敏感词过滤。

Chapter 5:Storage

共 10 节:

  1. Storage
  2. Goose Migrations
  3. SQLC
  4. Database Review
  5. Context
  6. Create User
  7. Create Chirp
  8. Collections and Singletons
  9. Get All Chirps
  10. Get Chirp

主要讲 PostgreSQL、数据库迁移、SQLC、context.Context、表关系和 REST 资源设计。

Chapter 6:Authentication

共 9 节:

  1. Authentication With Passwords
  2. Password Review
  3. Types of Authentication
  4. JWTs
  5. Authentication With JWTs
  6. JWT Review
  7. Revoking JWTs
  8. Refresh Tokens
  9. Cookies

主要讲密码哈希、登录、JWT、Bearer Token、Access Token、Refresh Token 和 Cookie。

Chapter 7:Authorization

共 3 节:

  1. Authorization
  2. Authentication vs. Authorization
  3. Delete Chirp

主要讲用户身份和用户权限的区别,以及如何限制用户只能操作自己的资源。

Chapter 8:Webhooks

共 3 节:

  1. Webhooks
  2. Webhooks Review
  3. API Keys

主要讲第三方系统回调、幂等性、Webhook 重试和 API Key 验证。

Chapter 9:Documentation

共 4 节:

  1. Documentation
  2. Documentation
  3. Sorting Chirps
  4. Adding a README

主要讲 Markdown API 文档、查询参数、排序和项目 README。


三、核心知识点详解

1. Go HTTP 服务器的基本结构

一个最基础的 Go HTTP 服务器由三部分组成:

package main

import (
	"log"
	"net/http"
)

func main() {
	mux := http.NewServeMux()

	server := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}

	log.Fatal(server.ListenAndServe())
}

其中:

http.Server
├── Addr:监听地址
└── Handler:收到请求后交给谁处理

ListenAndServe() 会:

  1. 监听 TCP 端口。
  2. 接收 HTTP 请求。
  3. 将请求交给 Handler。
  4. 为请求执行相应的处理逻辑。

课程使用 http.ServeMux 作为路由器,不依赖 Gin、Echo 或 Fiber。


2. Go 为什么适合写服务器

Go 的 HTTP 服务器会并发处理请求。可以从概念上理解为:

for 每个请求 {
	go handler(request)
}

真实实现由 net/http 管理,不需要手动给每个请求创建 goroutine。

与 Node.js 相比:

Node.js
└── 单线程事件循环
    └── 非常适合 I/O 密集任务

Go
└── goroutine 并发模型
    ├── 适合 I/O 密集任务
    └── 也更容易利用多核处理 CPU 密集任务

goroutine 比操作系统线程轻量,非常适合大量并发连接。


3. Handler、HandlerFunc 与 ResponseWriter

Handler 接口

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}

任何实现了 ServeHTTP 方法的类型,都可以成为 HTTP Handler。

type myHandler struct{}

func (h myHandler) ServeHTTP(
	w http.ResponseWriter,
	r *http.Request,
) {
	w.Write([]byte("hello"))
}

HandlerFunc

普通函数通常写成:

func handlerHealth(
	w http.ResponseWriter,
	r *http.Request,
) {
	w.WriteHeader(http.StatusOK)
	w.Write([]byte("OK"))
}

它的函数签名符合:

type HandlerFunc func(
	ResponseWriter,
	*Request,
)

两个参数的含义

*r http.Request

代表客户端发来的请求,包括:

  • HTTP Method
  • URL
  • Path
  • Query Parameters
  • Headers
  • Body
w http.ResponseWriter

代表即将发送给客户端的响应,可以设置:

  • 响应头
  • 状态码
  • 响应体

Go Handler 不直接 return Response,而是向 ResponseWriter 中写入响应。


4. 状态码和响应顺序

正确顺序是:

w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
w.Write(data)

必须先设置 Header,再调用 WriteHeader()

如果直接调用:

w.Write(data)

Go 会默认发送:

200 OK

常见状态码:

200 OK             请求成功
201 Created        成功创建资源
204 No Content     成功,但没有响应体
400 Bad Request    请求格式或参数错误
401 Unauthorized   未认证或认证凭证无效
403 Forbidden      已认证,但没有权限
404 Not Found      资源不存在
405 Method Not Allowed
500 Internal Server Error

5. 静态文件服务器

Go 可以直接将一个目录作为静态网站:

fileServer := http.FileServer(
	http.Dir("./app"),
)

mux.Handle(
	"/app/",
	http.StripPrefix("/app", fileServer),
)

例如:

./app/index.html
./app/assets/logo.png

可以通过:

http://localhost:8080/app/
http://localhost:8080/app/assets/logo.png

进行访问。

这里涉及两个概念:

FileServer

负责从文件系统读取文件。

StripPrefix

负责在访问文件系统前去除 URL 前缀。


6. 路由与 ServeMux Pattern

Go 1.22 之后,标准库支持方法级路由:

mux.HandleFunc(
	"GET /healthz",
	handlerHealth,
)

mux.HandleFunc(
	"POST /api/users",
	handlerCreateUser,
)

mux.HandleFunc(
	"DELETE /api/chirps/{chirpID}",
	handlerDeleteChirp,
)

路由 Pattern 的基本格式是:

[METHOD ] [HOST]/[PATH]

例如:

GET /articles
POST /articles
DELETE /articles/{articleID}

固定路径

"GET /about"

只匹配 /about

子树路径

"/images/"

可以匹配:

/images/
/images/logo.png
/images/icons/user.svg

路由优先级

匹配多个路由时,更具体、更长的 Pattern 优先。

路径参数

chirpID := r.PathValue("chirpID")

对应:

"GET /api/chirps/{chirpID}"

Go 的标准库已经可以完成大部分基础 REST API 路由,不一定必须引入第三方 Router。


7. Middleware 中间件

中间件是对 Handler 的包装:

func middleware(next http.Handler) http.Handler {
	return http.HandlerFunc(
		func(
			w http.ResponseWriter,
			r *http.Request,
		) {
			// 请求前执行

			next.ServeHTTP(w, r)

			// 请求后执行
		},
	)
}

常见用途:

日志记录
请求计数
身份认证
跨域处理
耗时统计
Panic 恢复
请求 ID
限流

例如访问计数:

func (cfg *apiConfig) middlewareMetrics(
	next http.Handler,
) http.Handler {
	return http.HandlerFunc(
		func(
			w http.ResponseWriter,
			r *http.Request,
		) {
			cfg.fileserverHits++
			next.ServeHTTP(w, r)
		},
	)
}

真实并发服务器中,共享计数器需要考虑并发安全,例如使用:

sync.Mutex

或者:

atomic.Int32

8. Stateful Handler:带依赖的处理器

真实 Handler 通常需要访问:

  • 数据库
  • JWT Secret
  • API Key
  • 配置
  • 日志器
  • 第三方服务

课程采用类似配置结构体的方式:

type apiConfig struct {
	db         *database.Queries
	jwtSecret  string
	polkaKey   string
	platform   string
}

然后把 Handler 写成方法:

func (cfg *apiConfig) handlerCreateUser(
	w http.ResponseWriter,
	r *http.Request,
) {
	// 可以访问 cfg.db
}

注册时:

mux.HandleFunc(
	"POST /api/users",
	cfg.handlerCreateUser,
)

这本质上是一种简单的依赖注入:

main
  ↓ 创建依赖
apiConfig
  ↓ 将依赖提供给
Handlers

比在 Handler 内部到处使用全局变量更容易测试和维护。


9. 单体架构与前后端分离

单体应用

一个服务
├── HTML
├── CSS / JS
├── API
└── 数据库访问

优点:

  • 开发和部署简单
  • 初期成本低
  • 一个程序即可运行

缺点:

  • 前后端耦合更明显
  • 后续独立扩容比较困难

前后端分离

浏览器

前端应用
  ↓ HTTP JSON API
Go 后端

数据库

优点:

  • 前后端可以独立开发
  • 可以独立部署和扩容
  • 同一套 API 可以供 Web、App、桌面端使用

缺点:

  • 部署更复杂
  • 需要处理 CORS、认证和接口版本

课程建议新项目可以先从单体开始,但在逻辑上保持前端和 API 解耦,为以后拆分留出空间。


10. API 命名空间

课程将不同接口分为:

/app/

静态前端。

/api/

提供给客户端使用的业务接口。

/admin/

管理员、监控和开发接口。

例如:

GET  /admin/metrics
POST /admin/reset

要注意:

/admin/ 只是路径组织方式,本身不提供安全保护。

真正的安全仍然需要身份认证和权限验证。


11. JSON 请求解析

客户端发送:

{
  "email": "lane@example.com",
  "password": "correct horse battery staple"
}

Go 中定义参数结构:

type parameters struct {
	Email    string `json:"email"`
	Password string `json:"password"`
}

解析请求体:

params := parameters{}

decoder := json.NewDecoder(r.Body)
if err := decoder.Decode(&params); err != nil {
	http.Error(
		w,
		"invalid JSON",
		http.StatusBadRequest,
	)
	return
}

关键规则:

  1. 字段必须导出,也就是首字母大写。
  2. json:"email" 是结构体标签。
  3. JSON 类型不匹配时会返回错误。
  4. 缺少字段时,该字段通常保持 Go 零值。
  5. 解析成功不代表业务参数一定合法。

因此还需要进一步验证:

if params.Email == "" {
	// 参数缺失
}

12. JSON 响应

可以封装公共函数:

func respondWithJSON(
	w http.ResponseWriter,
	code int,
	payload any,
) {
	w.Header().Set(
		"Content-Type",
		"application/json",
	)

	w.WriteHeader(code)

	if payload != nil {
		_ = json.NewEncoder(w).Encode(payload)
	}
}

错误响应:

func respondWithError(
	w http.ResponseWriter,
	code int,
	message string,
) {
	type errorResponse struct {
		Error string `json:"error"`
	}

	respondWithJSON(
		w,
		code,
		errorResponse{Error: message},
	)
}

调用:

respondWithError(
	w,
	http.StatusBadRequest,
	"Chirp is too long",
)

返回:

{
  "error": "Chirp is too long"
}

成功响应:

respondWithJSON(
	w,
	http.StatusOK,
	struct {
		Valid bool `json:"valid"`
	}{
		Valid: true,
	},
)

13. 参数校验与业务规则

课程中的 Chirp 最长 140 个字符。

大致流程:

解析 JSON

检查 body 是否存在

检查长度是否超过限制

清理不允许出现的单词

返回清理后的内容

应区分:

语法错误

JSON 本身无法解析:

400 Bad Request

参数错误

JSON 可以解析,但字段不符合要求:

400 Bad Request

服务器错误

数据库或内部组件出错:

500 Internal Server Error

客户端错误和服务器错误不能全部返回 500


14. 为什么需要数据库

内存中的变量:

users := []User{}

服务器重启后会全部消失。

直接写 JSON 文件虽然简单,但存在:

  • 并发写入冲突
  • 查询效率低
  • 数据关系难维护
  • 文件越来越大
  • 需要自己处理事务和索引

课程使用 PostgreSQL,将数据持久化到磁盘,并由数据库处理并发、查询和数据一致性。


15. 数据库迁移 Goose

数据库结构会不断变化,例如:

第一次:创建 users 表
第二次:增加 hashed_password
第三次:创建 chirps 表
第四次:创建 refresh_tokens 表

不能每次手动修改生产数据库,因此需要 migration。

示例:

-- +goose Up

CREATE TABLE users (
    id UUID PRIMARY KEY,
    created_at TIMESTAMP NOT NULL,
    updated_at TIMESTAMP NOT NULL,
    email TEXT UNIQUE NOT NULL
);

-- +goose Down

DROP TABLE users;

Up 表示应用修改。

Down 表示回滚修改。

迁移文件应该:

  • 进入 Git
  • 按顺序执行
  • 尽量只负责一次结构变更
  • 同时提供回滚方案

16. SQLC:类型安全 SQL

传统方式:

row := db.QueryRow(
	"SELECT id, email FROM users WHERE id = $1",
	id,
)

查询结果和 Go 类型之间容易发生不匹配。

SQLC 的工作方式:

编写 SQL

运行 sqlc generate

生成 Go 函数和结构体

例如 SQL:

-- name: GetUser :one

SELECT *
FROM users
WHERE id = $1;

可能生成:

func (q *Queries) GetUser(
	ctx context.Context,
	id uuid.UUID,
) (User, error)

优点:

  • 保留原生 SQL
  • 编译期类型检查更强
  • 减少手写扫描代码
  • 查询结果自动转成 Go 类型

课程使用 PostgreSQL、Goose 和 SQLC 组成数据访问层。


17. Context 的作用

数据库调用通常接收:

ctx context.Context

Handler 中可以使用:

user, err := cfg.db.GetUser(
	r.Context(),
	userID,
)

r.Context() 与当前 HTTP 请求绑定。

当客户端取消请求、连接断开或者请求超时时,Context 可以通知下游数据库操作停止工作。

Context 适合传递:

  • 取消信号
  • Deadline
  • Timeout
  • Request-scoped 数据

不适合拿来传递普通业务参数或全局依赖。


18. REST:集合资源与单个资源

资源通常使用复数名词。

创建资源

POST /api/chirps

获取资源集合

GET /api/chirps

获取单个资源

GET /api/chirps/{chirpID}

删除单个资源

DELETE /api/chirps/{chirpID}

相比:

POST /createChirp
GET  /getAllChirps

REST 更倾向于:

HTTP Method 表达动作
URL Path 表达资源

/api/chirps 是集合,/api/chirps/{id} 是某个具体资源。


19. 密码不能明文保存

错误设计:

users
├── email
└── password = "123456"

数据库泄漏后,攻击者可以直接看到所有密码。

正确设计:

用户密码

Argon2id

哈希值

保存到数据库

课程使用 Argon2id:

hash, err := argon2id.CreateHash(password)

登录时:

match, err :=
	argon2id.ComparePasswordAndHash(
		password,
		storedHash,
	)

密码哈希必须:

  • 是单向运算
  • 自动加入随机盐
  • 计算成本足够高
  • 专门用于密码存储

不应直接使用:

MD5
SHA-1
SHA-256

它们计算太快,不适合直接进行密码存储。


20. 登录过程

登录接口:

POST /api/login

请求:

{
  "email": "lane@example.com",
  "password": "password"
}

服务端流程:

根据 email 查询用户

获取 hashed_password

校验密码

创建 Access Token

创建 Refresh Token

返回 Token

出于安全考虑,下面两种情况通常返回相同错误:

邮箱不存在
密码错误

例如:

{
  "error": "Incorrect email or password"
}

这样可以减少攻击者枚举注册邮箱的机会。


21. JWT 是什么

JWT 通常包含三个部分:

Header.Payload.Signature

例如:

xxxxx.yyyyy.zzzzz

描述签名算法和 Token 类型。

Payload

包含 Claims,例如:

{
  "sub": "user-id",
  "iss": "chirpy",
  "exp": 1750000000
}

常见 Claims:

sub:Subject,Token 对应的用户
iss:Issuer,Token 签发者
exp:Expiration,过期时间
iat:Issued At,签发时间

Signature

服务器使用 Secret 对 Header 和 Payload 进行签名。

JWT 的重要特点:

已签名,但没有加密

用户不能在不破坏签名的情况下修改 Payload,但任何拿到 JWT 的人通常都能读取 Payload。

因此绝不能在 JWT 中存放:

  • 用户密码
  • 私钥
  • 银行卡信息
  • 其他敏感数据

22. Bearer Token

客户端通常通过 Header 发送 JWT:

Authorization: Bearer eyJhbGciOi...

服务端需要:

  1. 查找 Authorization Header。
  2. 检查是否以 Bearer 开头。
  3. 提取 Token。
  4. 验证签名。
  5. 验证过期时间。
  6. 读取用户 ID。

可以封装:

func GetBearerToken(
	headers http.Header,
) (string, error) {
	value := headers.Get("Authorization")

	const prefix = "Bearer "

	if !strings.HasPrefix(value, prefix) {
		return "", errors.New(
			"missing bearer token",
		)
	}

	return strings.TrimSpace(
		strings.TrimPrefix(value, prefix),
	), nil
}

23. Access Token 与 Refresh Token

Access Token

通常是 JWT:

无状态
生命周期短
不能轻易撤销
用于访问受保护资源

例如:

POST /api/chirps
PUT  /api/users
DELETE /api/chirps/{id}

Refresh Token

通常是高强度随机字符串:

有状态
生命周期长
保存在数据库
可以撤销
只能用来换取 Access Token

工作流程:

登录

Access Token + Refresh Token

Access Token 过期

POST /api/refresh

获得新的 Access Token

退出登录或发现 Token 泄漏:

POST /api/revoke

数据库中的 Refresh Token 可以设置:

revoked_at = 当前时间

课程使用短期 JWT Access Token 和长期、可撤销的 Refresh Token 来平衡性能、安全性和用户体验。


24. Authentication 与 Authorization

Authentication:认证

回答:

你是谁?

例如:

  • 密码
  • JWT
  • Session
  • API Key
  • OAuth
  • Magic Link

Authorization:授权

回答:

你能做什么?

例如:

用户已经登录
但只能删除自己发布的 Chirp

删除流程:

验证 Access Token

获取当前 userID

获取 chirp

判断 chirp.userID == currentUserID

允许或拒绝删除

认证成功不等于拥有所有权限。


25. Webhook

Webhook 是外部服务主动发送给服务器的 HTTP 请求。

例如:

用户在支付平台完成付款

支付平台向 Chirpy 发送 Webhook

Chirpy 将用户升级为付费用户

Webhook 和 WebSocket 不同:

Webhook
├── 普通 HTTP 请求
├── 外部服务主动调用
└── 一次请求对应一次事件

WebSocket
├── 持久连接
├── 双向通信
└── 适合实时消息

Webhook 的关键问题

重试

外部服务没有收到成功响应时,通常会重新发送。

幂等性

重复处理同一个事件不能产生错误结果。

例如升级用户:

UPDATE users
SET is_chirpy_red = true
WHERE id = $1;

重复执行仍然是 true,因此相对容易保持幂等。

正确确认

只有处理成功后才返回 2xx

一旦返回 2xx,外部服务通常会认为事件已经成功处理并停止重试。


26. API Key

如果 Webhook 没有认证,任何人都可以伪造请求。

API Key 模式:

Authorization: ApiKey THE_KEY

服务端:

读取 Header

提取 API Key

与环境变量中的 Key 比较

匹配后处理 Webhook

密钥应该保存在:

.env
环境变量
Secret Manager

不应该:

硬编码进代码
提交到 Git
打印到日志
返回给普通客户端

在课程中,API Key 用于验证 Webhook 请求确实来自模拟支付服务 Polka。


27. 查询参数和排序

URL:

GET /api/chirps?sort=desc

Go 中读取:

sortOrder :=
	r.URL.Query().Get("sort")

课程支持:

sort=asc
sort=desc

没有提供时默认升序。

查询参数适合表示:

  • 排序
  • 筛选
  • 分页
  • 搜索
  • 可选配置

不需要为每种排序创建一个新接口:

不推荐:
GET /api/chirps-oldest
GET /api/chirps-newest

推荐:
GET /api/chirps?sort=asc
GET /api/chirps?sort=desc

课程使用 sort.Slice 在内存中对 Chirps 排序。


28. API 文档应该写什么

至少应该说明:

接口路径
HTTP 方法
请求 Header
Path 参数
Query 参数
请求 JSON
成功响应
错误响应
状态码
认证方式

示例:

## Create Chirp

POST /api/chirps

### Authentication

Authorization: Bearer <access-token>

### Request

{
  "body": "Hello world"
}

### Response

201 Created

{
  "id": "...",
  "body": "Hello world",
  "user_id": "..."
}

课程比较了:

  • 手写 Markdown
  • Swagger
  • Postman
  • Godoc
  • GraphQL 文档工具

对于小型学习项目,课程建议优先从 Markdown 和 README 开始。


四、这门课真正需要掌握的 15 个重点

完成课程后,至少要能独立解释:

  1. http.ServerServeMux 和 Handler 的关系。
  2. 为什么 Handler 接收 ResponseWriter 而不是返回 Response。
  3. Go 如何并发处理 HTTP 请求。
  4. 如何使用方法和路径注册路由。
  5. Middleware 如何包装 Handler。
  6. 如何通过结构体给 Handler 注入数据库和配置。
  7. 如何解析和返回 JSON。
  8. 如何正确使用状态码。
  9. 为什么需要 PostgreSQL,而不是只把数据放在内存中。
  10. Goose migration 解决什么问题。
  11. SQLC 如何把 SQL 转成类型安全的 Go 代码。
  12. 密码为什么必须使用 Argon2id 等密码哈希算法。
  13. JWT 为什么是签名而不是加密。
  14. Access Token 和 Refresh Token 的区别。
  15. Webhook 为什么必须考虑认证、重试和幂等性。

五、课程主线总结

这门课最重要的不是记住某个函数,而是理解一个后端请求完整经历了什么:

1. 客户端发起请求
2. ServeMux 匹配路由
3. Middleware 进行公共处理
4. Handler 解析参数
5. 校验 JSON 和业务规则
6. 验证 JWT 或 API Key
7. 检查用户权限
8. 使用 SQLC 操作 PostgreSQL
9. 根据结果选择状态码
10. 返回 JSON

它实际上是一门:

Go 标准库 HTTP
+ REST API
+ PostgreSQL
+ 后端认证授权
+ Webhook

的综合项目课,而不仅仅是一门介绍 net/http 函数的课程。


六、仅看知识点会遗漏什么

直接阅读这份笔记,可以理解课程大约 70%~80% 的概念,但以下能力主要来自实际编码:

  • 路由 Pattern 写错时如何排查
  • Header 和状态码顺序错误
  • SQL migration 执行失败
  • UUID 和数据库类型转换
  • JWT 过期和签名错误
  • context.Context 的实际传递
  • Handler 之间如何复用代码
  • 如何组织 main.gointernal/auth、数据库包和 Handler 文件
  • 如何使用 curl、Bruno 或 Postman 调试接口

因此最值得亲手完成的部分是:

创建 HTTP Server
→ JSON API
→ PostgreSQL + SQLC
→ Password Login
→ JWT + Refresh Token
→ Authorization
→ Webhook
创建于 2026/8/1 更新于 2026/8/3