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