package main import ( "encoding/json" "fmt" "io" "net/http" "net/http/httptest" "net/url" "os" "strings" "time" ) // Greeting 是服务端返回的 JSON 结构。 type Greeting struct { Message string `json:"message"` } // greetingHandler 是 HTTP 服务端的处理函数。 func greetingHandler(w http.ResponseWriter, r *http.Request) { // 本课只接受 GET。状态码不是 Go 函数的 error 返回值。 if r.Method != http.MethodGet { w.Header().Set("Allow", http.MethodGet) http.Error(w, "只支持 GET", http.StatusMethodNotAllowed) return } name := strings.TrimSpace(r.URL.Query().Get("name")) if name == "" { http.Error(w, "name 不能为空", http.StatusBadRequest) return } w.Header().Set("Content-Type", "application/json; charset=utf-8") // 首次写入响应体时会默认发送 200 状态码。 if err := json.NewEncoder(w).Encode(Greeting{Message: "你好," + name + "!"}); err != nil { // 响应可能已开始,不能保证此时还能改状态码。 fmt.Fprintln(os.Stderr, "写入响应失败:", err) } } // fetchGreeting 是客户端:发送请求、检查状态、读取并解析 JSON。 func fetchGreeting(client *http.Client, baseURL, name string) (Greeting, error) { params := url.Values{} params.Set("name", name) // 对中文、空格和特殊字符做 URL 编码。 response, err := client.Get(baseURL + "/hello?" + params.Encode()) if err != nil { return Greeting{}, fmt.Errorf("发送请求:%w", err) } defer response.Body.Close() // 只在请求成功后关闭响应体。 if response.StatusCode != http.StatusOK { return Greeting{}, fmt.Errorf("服务端返回状态码 %d", response.StatusCode) } // 为示例设置大小上限,避免无限读取响应体。 const maxBody = 64 * 1024 data, err := io.ReadAll(io.LimitReader(response.Body, maxBody+1)) if err != nil { return Greeting{}, fmt.Errorf("读取响应:%w", err) } if len(data) > maxBody { return Greeting{}, fmt.Errorf("响应内容超过 %d 字节", maxBody) } var result Greeting if err := json.Unmarshal(data, &result); err != nil { return Greeting{}, fmt.Errorf("解析 JSON:%w", err) } return result, nil } func run() error { // 新建路由器:只将 /hello 交给我们的处理函数。 mux := http.NewServeMux() mux.HandleFunc("/hello", greetingHandler) // 本课使用本机临时测试服务器,自动选择可用端口,不访问外网。 server := httptest.NewServer(mux) defer server.Close() fmt.Println("本地临时服务:", server.URL) // 设置超时,避免请求无限等待。 client := &http.Client{Timeout: 3 * time.Second} result, err := fetchGreeting(client, server.URL, "小朱") if err != nil { return err } fmt.Println("客户端收到:", result.Message) fmt.Println("演示结束,临时服务即将关闭") return nil } func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, "请求失败:", err) os.Exit(1) } }