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))
}

接口与数据流

GET /users POST /users GET /users/{id} PUT /users/{id} DELETE /users/{id} http.ServeMux /users → 集合操作 /users/ → 单资源操作 方法分发 GET → 查询 POST → 创建 PUT → 更新 DELETE → 删除 UserStore map[int]User sync.RWMutex GetAll / Get Create / Update Delete 内存存储,重启即清空 JSON Response 200 / 201 / 204 / 400 / 404

运行与验证

# 启动服务
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 两条记录。