2. RESTful API 基础
任务说明
实现一个基于 net/http 标准库的 RESTful API 服务,对内存中的用户数据执行增删改查操作。API 需支持 GET /users(获取全部用户)、GET /users/{id}(获取单个用户)、POST /users(新增用户)、PUT /users/{id}(更新用户)、DELETE /users/{id}(删除用户)。所有数据仅保存在进程内存中,服务重启后数据重置。
核心实现
package main
import (
"encoding/json"
"log"
"net/http"
"strconv"
"strings"
"sync"
)
type User struct {
ID int `json:"id"`
Name string `json:"name"`
Age int `json:"age"`
}
type UserStore struct {
mu sync.RWMutex
users map[int]User
nextID int
}
func NewUserStore() *UserStore {
return &UserStore{
users: make(map[int]User),
nextID: 1,
}
}
func (s *UserStore) GetAll() []User {
s.mu.RLock()
defer s.mu.RUnlock()
result := make([]User, 0, len(s.users))
for _, u := range s.users {
result = append(result, u)
}
return result
}
func (s *UserStore) Get(id int) (User, bool) {
s.mu.RLock()
defer s.mu.RUnlock()
u, ok := s.users[id]
return u, ok
}
func (s *UserStore) Create(u User) User {
s.mu.Lock()
defer s.mu.Unlock()
u.ID = s.nextID
s.nextID++
s.users[u.ID] = u
return u
}
func (s *UserStore) Update(id int, u User) (User, bool) {
s.mu.Lock()
defer s.mu.Unlock()
_, ok := s.users[id]
if !ok {
return User{}, false
}
u.ID = id
s.users[id] = u
return u, true
}
func (s *UserStore) Delete(id int) bool {
s.mu.Lock()
defer s.mu.Unlock()
_, ok := s.users[id]
if ok {
delete(s.users, id)
}
return ok
}
func writeJSON(w http.ResponseWriter, status int, data interface{}) {
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(status)
json.NewEncoder(w).Encode(data)
}
func writeError(w http.ResponseWriter, status int, msg string) {
writeJSON(w, status, map[string]string{"error": msg})
}
func parseID(path string) (int, bool) {
parts := strings.Split(strings.Trim(path, "/"), "/")
if len(parts) != 2 || parts[0] != "users" {
return 0, false
}
id, err := strconv.Atoi(parts[1])
if err != nil || id <= 0 {
return 0, false
}
return id, true
}
func main() {
store := NewUserStore()
// 预置两条示例数据
store.Create(User{Name: "Alice", Age: 30})
store.Create(User{Name: "Bob", Age: 25})
mux := http.NewServeMux()
mux.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
switch r.Method {
case http.MethodGet:
writeJSON(w, http.StatusOK, store.GetAll())
case http.MethodPost:
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON body")
return
}
if u.Name == "" {
writeError(w, http.StatusBadRequest, "name is required")
return
}
created := store.Create(u)
writeJSON(w, http.StatusCreated, created)
default:
w.Header().Set("Allow", "GET, POST")
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
}
})
mux.HandleFunc("/users/", func(w http.ResponseWriter, r *http.Request) {
id, ok := parseID(r.URL.Path)
if !ok {
writeError(w, http.StatusBadRequest, "invalid user ID")
return
}
switch r.Method {
case http.MethodGet:
u, found := store.Get(id)
if !found {
writeError(w, http.StatusNotFound, "user not found")
return
}
writeJSON(w, http.StatusOK, u)
case http.MethodPut:
var u User
if err := json.NewDecoder(r.Body).Decode(&u); err != nil {
writeError(w, http.StatusBadRequest, "invalid JSON body")
return
}
if u.Name == "" {
writeError(w, http.StatusBadRequest, "name is required")
return
}
updated, found := store.Update(id, u)
if !found {
writeError(w, http.StatusNotFound, "user not found")
return
}
writeJSON(w, http.StatusOK, updated)
case http.MethodDelete:
if !store.Delete(id) {
writeError(w, http.StatusNotFound, "user not found")
return
}
w.WriteHeader(http.StatusNoContent)
default:
w.Header().Set("Allow", "GET, PUT, DELETE")
writeError(w, http.StatusMethodNotAllowed, "method not allowed")
}
})
log.Println("Server listening on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
接口与数据流
运行与验证
# 启动服务
go run main.go
# 验证接口(另开终端执行)
curl -s http://localhost:8080/users
# 预期输出:[{"id":1,"name":"Alice","age":30},{"id":2,"name":"Bob","age":25}]
curl -s http://localhost:8080/users/1
# 预期输出:{"id":1,"name":"Alice","age":30}
curl -s -X POST http://localhost:8080/users \
-H "Content-Type: application/json" \
-d '{"name":"Carol","age":28}'
# 预期输出:{"id":3,"name":"Carol","age":28}
curl -s -X PUT http://localhost:8080/users/2 \
-H "Content-Type: application/json" \
-d '{"name":"Bobby","age":26}'
# 预期输出:{"id":2,"name":"Bobby","age":26}
curl -s -X DELETE http://localhost:8080/users/1
# 预期输出:(无内容,HTTP 204)
curl -s http://localhost:8080/users/1
# 预期输出:{"error":"user not found"}(HTTP 404)
判定标准:上述 curl 命令返回的 HTTP 状态码与 JSON 内容符合注释中的预期输出,且 GET /users 能反映删除后的最新数据,即视为实现正确。服务重启后数据恢复为预置的 Alice 和 Bob 两条记录。