3. JSON 请求与响应处理

JSON 请求与响应处理 学习卡片

任务说明

实现一个 HTTP 服务,接收客户端发送的 JSON 请求体,解析后返回 JSON 响应。要求包含两类错误处理:请求体格式非法时返回 400 状态码及错误信息;请求体合法但业务校验失败时返回 422 状态码及具体字段错误。通过 curl 命令验证完整闭环。

核心实现

package main

import (
    "encoding/json"
    "errors"
    "fmt"
    "log"
    "net/http"
    "strings"
)

// User 定义请求与响应共用的数据结构
type User struct {
    Name  string `json:"name"`
    Email string `json:"email"`
    Age   int    `json:"age"`
}

// ErrorResponse 统一错误响应结构
type ErrorResponse struct {
    Error string `json:"error"`
}

// validateUser 业务校验:name非空,email包含@,age在0-120之间
func validateUser(u *User) error {
    var msgs []string
    if strings.TrimSpace(u.Name) == "" {
        msgs = append(msgs, "name is required")
    }
    if !strings.Contains(u.Email, "@") {
        msgs = append(msgs, "email must contain @")
    }
    if u.Age < 0 || u.Age > 120 {
        msgs = append(msgs, "age must be between 0 and 120")
    }
    if len(msgs) > 0 {
        return errors.New(strings.Join(msgs, "; "))
    }
    return nil
}

func userHandler(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")

    // 仅接受 POST 方法
    if r.Method != http.MethodPost {
        w.WriteHeader(http.StatusMethodNotAllowed)
        json.NewEncoder(w).Encode(ErrorResponse{Error: "method not allowed"})
        return
    }

    // 解析 JSON 请求体
    var req User
    decoder := json.NewDecoder(r.Body)
    if err := decoder.Decode(&req); err != nil {
        w.WriteHeader(http.StatusBadRequest)
        json.NewEncoder(w).Encode(ErrorResponse{Error: "invalid JSON body: " + err.Error()})
        return
    }

    // 业务校验
    if err := validateUser(&req); err != nil {
        w.WriteHeader(http.StatusUnprocessableEntity)
        json.NewEncoder(w).Encode(ErrorResponse{Error: err.Error()})
        return
    }

    // 构造成功响应(回显数据并附加状态字段)
    resp := map[string]interface{}{
        "status": "ok",
        "user":   req,
    }
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(resp)
}

func main() {
    http.HandleFunc("/user", userHandler)
    addr := ":8080"
    fmt.Printf("Server listening on %s\n", addr)
    log.Fatal(http.ListenAndServe(addr, nil))
}

接口与错误处理逻辑

// 请求示例
POST /user
Content-Type: application/json

{"name":"Alice","email":"alice@example.com","age":30}

// 成功响应 (200)
{"status":"ok","user":{"name":"Alice","email":"alice@example.com","age":30}}

// 非法JSON (400)
{"error":"invalid JSON body: invalid character 'x' looking for beginning of value"}

// 业务校验失败 (422)
{"error":"name is required; email must contain @; age must be between 0 and 120"}

运行与验证

# 1. 启动服务
go run main.go

# 2. 验证成功场景(预期返回200及回显数据)
curl -i -X POST http://localhost:8080/user \
  -H "Content-Type: application/json" \
  -d '{"name":"Alice","email":"alice@example.com","age":30}'

# 3. 验证非法JSON(预期返回400及错误信息)
curl -i -X POST http://localhost:8080/user \
  -H "Content-Type: application/json" \
  -d '{invalid json}'

# 4. 验证业务校验失败(预期返回422及字段错误)
curl -i -X POST http://localhost:8080/user \
  -H "Content-Type: application/json" \
  -d '{"name":"","email":"bad-email","age":200}'

判定标准

  • 成功请求返回 HTTP 200,响应体包含 status:"ok" 及回显的 user 对象;
  • 请求体无法解析为合法 JSON 时返回 HTTP 400,错误信息以 invalid JSON body: 开头;
  • JSON 合法但字段不满足校验规则时返回 HTTP 422,错误信息列出所有未通过的具体字段原因;
  • 非 POST 请求返回 HTTP 405。
客户端 (curl) HTTP 服务 :8080 POST /user JSON 解码 json.Decoder 业务校验 validateUser JSON 响应 json.Encoder 错误响应 ① JSON 请求体 解码失败 → 400 校验失败 → 422 ② JSON 响应 (200/400/422) 状态码约定 200 — 成功 400 — JSON 格式错误 422 — 业务校验失败