案例5:Go 基础——实现简单的HTTP接口(新手友好)
案例5:Go 基础——实现简单的HTTP接口(新手友好)
需求描述
用Go语言实现一个简单的HTTP接口服务,包含3个核心接口:GET请求获取所有用户信息、GET请求根据ID获取单个用户信息、POST请求添加新用户,要求使用Go原生net/http包(无需额外依赖),代码结构清晰,附带详细注释,可直接运行,返回JSON格式响应,处理请求参数错误、用户ID不存在等异常情况,同时添加接口访问日志,方便调试。
Claude 输入
“用Go语言实现一个简单的HTTP接口服务,需求如下:
- 使用Go原生net/http包,无需额外依赖;
- 包含3个接口:GET /users(获取所有用户信息)、GET /users/{id}(根据ID获取单个用户)、POST /users(添加新用户,请求体为JSON格式,包含name和age字段);3. 所有接口返回JSON格式响应,成功返回200状态码,异常返回对应错误状态码(如400、404);
- 处理异常:请求参数错误、用户ID不存在、POST请求体解析失败、请求方法错误;
- 添加接口访问日志,打印请求方法、请求路径、访问时间;
- 代码结构清晰,附带详细注释,可直接运行;
- 用切片模拟数据库存储用户信息,用户包含id(int)、name(string)、age(int)字段。”
生成代码(参考)
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"strconv"
"time"
)
// User 定义用户结构体,对应JSON格式
type User struct {
ID int `json:"id"` // 用户ID,唯一标识
Name string `json:"name"` // 用户名
Age int `json:"age"` // 用户年龄
}
// 模拟数据库,用切片存储用户信息(实际开发中替换为真实数据库)
var users = []User{
{ID: 1, Name: "张三", Age: 20},
{ID: 2, Name: "李四", Age: 22},
{ID: 3, Name: "王五", Age: 21},
}
// 全局变量,用于生成新用户的ID(模拟自增主键)
var nextUserID = 4
// 日志中间件:记录接口访问信息(请求方法、路径、访问时间)
func loggingMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// 记录访问时间、请求方法、请求路径
log.Printf("[%s] %s %s", time.Now().Format("2006-01-02 15:04:05"), r.Method, r.URL.Path)
// 调用下一个处理器(实际接口逻辑)
next.ServeHTTP(w, r)
})
}
// getAllUsersHandler 处理GET /users请求,返回所有用户信息
func getAllUsersHandler(w http.ResponseWriter, r *http.Request) {
// 只允许GET方法,其他方法返回405 Method Not Allowed
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{"error": "只允许GET请求"})
return
}
// 设置响应头为JSON格式
w.Header().Set("Content-Type", "application/json")
// 返回200状态码和所有用户信息
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 200,
"msg": "success",
"data": users,
})
}
// getUserByIDHandler 处理GET /users/{id}请求,根据ID获取单个用户
func getUserByIDHandler(w http.ResponseWriter, r *http.Request) {
// 只允许GET方法
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{"error": "只允许GET请求"})
return
}
// 提取URL路径中的id参数(如/users/1,提取1)
idStr := r.PathValue("id")
// 将字符串id转换为int类型
id, err := strconv.Atoi(idStr)
if err != nil {
// 转换失败,说明id不是有效数字,返回400 Bad Request
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "无效的用户ID,需输入数字"})
return
}
// 遍历切片,查找对应ID的用户
var targetUser User
found := false
for _, user := range users {
if user.ID == id {
targetUser = user
found = true
break
}
}
// 若未找到用户,返回404 Not Found
if !found {
w.WriteHeader(http.StatusNotFound)
json.NewEncoder(w).Encode(map[string]string{"error": "用户不存在"})
return
}
// 找到用户,返回200状态码和用户信息
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 200,
"msg": "success",
"data": targetUser,
})
}
// addUserHandler 处理POST /users请求,添加新用户
func addUserHandler(w http.ResponseWriter, r *http.Request) {
// 只允许POST方法
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
json.NewEncoder(w).Encode(map[string]string{"error": "只允许POST请求"})
return
}
// 解析请求体(JSON格式),绑定到User结构体
var newUser User
err := json.NewDecoder(r.Body).Decode(&newUser)
defer r.Body.Close() // 关闭请求体,避免资源泄漏
if err != nil {
// 解析失败,说明请求体格式错误,返回400 Bad Request
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "请求体格式错误,需为JSON格式,包含name和age字段"})
return
}
// 验证请求参数:name不能为空,age需为正数
if newUser.Name == "" || newUser.Age <= 0 {
w.WriteHeader(http.StatusBadRequest)
json.NewEncoder(w).Encode(map[string]string{"error": "参数错误:name不能为空,age需为正数"})
return
}
// 给新用户分配ID(自增),添加到切片中
newUser.ID = nextUserID
users = append(users, newUser)
nextUserID++ // ID自增,为下一个用户做准备
// 返回200状态码和添加成功的用户信息
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(map[string]interface{}{
"code": 200,
"msg": "用户添加成功",
"data": newUser,
})
}
func main() {
// 注册路由,绑定接口路径和对应的处理器
// 使用日志中间件,所有请求都会先经过日志处理
http.Handle("/users", loggingMiddleware(http.HandlerFunc(getAllUsersHandler)))
// 路由参数:{id}表示动态ID,Go 1.22+支持PathValue提取
http.Handle("/users/{id}", loggingMiddleware(http.HandlerFunc(getUserByIDHandler)))
http.Handle("/users", loggingMiddleware(http.HandlerFunc(addUserHandler)))
// 启动HTTP服务,监听8080端口
log.Println("HTTP服务已启动,监听端口:8080")
log.Println("可用接口:")
log.Println("1. GET http://localhost:8080/users —— 获取所有用户")
log.Println("2. GET http://localhost:8080/users/1 —— 获取ID为1的用户")
log.Println("3. POST http://localhost:8080/users —— 添加新用户(请求体JSON:{\"name\":\"xxx\",\"age\":xx})")
// 启动服务,若失败则打印错误并退出
err := http.ListenAndServe(":8080", nil)
if err != nil {
log.Fatalf("服务启动失败:%v", err)
}
}
效果测试与调整
- 将生成的代码复制到Go编辑器(如Goland、VS Code),保存为“main.go”文件;
- 打开终端,进入代码所在目录,执行命令“go run main.go”,启动HTTP服务,终端会显示服务启动信息及可用接口;
- 测试接口(可使用Postman、curl或浏览器):测试GET /users:浏览器访问“http://localhost:8080/users”,或curl命令“curl http://localhost:8080/users”,可看到所有用户的JSON响应;
- 测试GET /users/{id}:访问“http://localhost:8080/users/1”,可获取ID为1的用户信息;若访问“http://localhost:8080/users/10”(不存在的ID),会返回404错误;若访问“http://localhost:8080/users/abc”(无效ID),会返回400错误;
- 测试POST /users:使用Postman发送POST请求,地址“http://localhost:8080/users”,请求体为JSON格式(如{"name":"赵六","age":23}),请求头设置“Content-Type: application/json”,发送后会返回添加成功的响应,再次访问GET /users可看到新添加的用户;若请求体格式错误(如缺少age字段),会返回400错误。
- 异常测试:故意发送非GET/POST方法的请求(如PUT /users),会返回405错误;发送POST请求时,若age设为负数,会返回参数错误提示;
- 调整需求:若想添加“删除用户”接口,可回到Claude Code输入区,补充“给HTTP接口服务添加DELETE /users/{id}接口,根据ID删除用户,处理用户不存在、ID无效的异常,添加访问日志”,重新生成代码即可;若想使用gin框架(而非原生net/http包)实现,可补充“使用gin框架重写该HTTP接口服务,保留所有功能,代码附带注释”。