首页 / Go 语言入门教程 / net/http 基础

Go 语言入门教程

net/http 基础

本教程共 80 篇 · 第 76 篇 · 更新于 2026-07-27 · 约 8 分钟阅读

GoGo 入门教程net/httpHTTPHandleFuncListenAndServeHandlerServeMux

76. net/http 基础

本节目标:学会用标准库搭 HTTP 服务、发 HTTP 请求,理解 Handler 接口和路由分发。

最简 HTTP 服务

Go 标准库写 HTTP 服务极简,几行就能跑:

package main

import (
	"fmt"
	"net/http"
)

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Hello, World!")
	})

	http.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "API 响应")
	})

	fmt.Println("服务启动在 :8080")
	http.ListenAndServe(":8080", nil)
}

访问 http://localhost:8080 就能看到 Hello, World!。

  • HandleFunc 注册路由
  • ListenAndServe 启动服务监听端口
  • 第二个参数传 nil 表示用默认的 DefaultServeMux

Handler 接口

http.Handler 是一个接口:

type Handler interface {
	ServeHTTP(w ResponseWriter, r *Request)
}

任何实现了 ServeHTTP 方法的类型都能当 Handler。HandleFunc 其实是把普通函数适配成 Handler:

// 这两种写法等价
http.HandleFunc("/", myHandler)

type myHandler struct{}
func (h myHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprint(w, "hello")
}
http.Handle("/", myHandler{})

读取请求信息

*http.Request 包含请求的所有信息:

func handler(w http.ResponseWriter, r *http.Request) {
	// 方法
	method := r.Method // GET, POST 等

	// 路径
	path := r.URL.Path

	// 查询参数
	name := r.URL.Query().Get("name")

	// 请求头
	contentType := r.Header.Get("Content-Type")

	// 请求体(POST 等)
	body, _ := io.ReadAll(r.Body)
	defer r.Body.Close()

	// 路径参数(Go 1.22+ ServeMux 支持)
	// http.HandleFunc("/users/{id}", handler)
	// id := r.PathValue("id")

	fmt.Fprintf(w, "method=%s path=%s name=%s", method, path, name)
}

Go 1.22 增强路由

Go 1.22 的 ServeMux 支持路径参数和方法匹配:

mux := http.NewServeMux()

// 匹配 GET /users/123
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	fmt.Fprintf(w, "用户 %s", id)
})

// 匹配 POST /users
mux.HandleFunc("POST /users", func(w http.ResponseWriter, r *http.Request) {
	// 创建用户
})

http.ListenAndServe(":8080", mux)

以前要靠第三方框架(Gin、Echo)才有的功能,现在标准库自带了。

写响应

func handler(w http.ResponseWriter, r *http.Request) {
	// 设置状态码
	w.WriteHeader(http.StatusOK) // 200

	// 设置响应头
	w.Header().Set("Content-Type", "application/json")

	// 写响应体
	w.Write([]byte(`{"msg":"hello"}`))
}
Warning

WriteHeader 必须在 Write 之前调用。调了 Write 后再设状态码无效,默认就是 200。

返回 JSON

type Response struct {
	Code int    `json:"code"`
	Msg  string `json:"msg"`
}

func handler(w http.ResponseWriter, r *http.Request) {
	resp := Response{Code: 200, Msg: "success"}

	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(resp) // 直接编码到 ResponseWriter
}

http.Client 发请求

发 HTTP 请求用 http.Client

func main() {
	// 简单 GET
	resp, err := http.Get("https://httpbin.org/get")
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	body, _ := io.ReadAll(resp.Body)
	fmt.Println(string(body))
	fmt.Println("状态码:", resp.StatusCode)
}

需要更多控制时用 http.NewRequest

// POST 请求带 JSON body
reqBody := strings.NewReader(`{"name":"Tom"}`)
resp, err := http.Post("https://httpbin.org/post", "application/json", reqBody)

// 自定义请求
req, _ := http.NewRequest("GET", "https://httpbin.org/get", nil)
req.Header.Set("Authorization", "Bearer mytoken")
req.Header.Set("X-Custom", "hello")

client := &http.Client{Timeout: 10 * time.Second}
resp, err := client.Do(req)
Tip

生产环境别用 http.Get(它用默认 Client,没超时)。一定要自己建 Client 并设 Timeout,否则请求卡住会拖垮服务:

client := &http.Client{
	Timeout: 30 * time.Second,
}

带 context 的请求

长请求要能取消,用 NewRequestWithContext

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

req, _ := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/delay/10", nil)
resp, err := http.DefaultClient.Do(req)
if err != nil {
	fmt.Println("超时或出错:", err)
}

5 秒没返回自动取消,不会傻等。

静态文件服务

// 暴露 ./static 目录
fs := http.FileServer(http.Dir("./static"))
http.Handle("/static/", http.StripPrefix("/static/", fs))

访问 http://localhost:8080/static/logo.png 就能拿到 ./static/logo.png

优雅关闭

生产服务要能优雅关闭(处理完现有请求再退出):

srv := &http.Server{Addr: ":8080", Handler: mux}

go func() {
	if err := srv.ListenAndServe(); err != http.ErrServerClosed {
		log.Fatal(err)
	}
}()

// 等待中断信号
stop := make(chan os.Signal, 1)
signal.Notify(stop, os.Interrupt)
<-stop

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
srv.Shutdown(ctx) // 等现有请求处理完

小结

  • HandleFunc 注册路由,ListenAndServe 启动服务
  • Handler 接口只需实现 ServeHTTP 方法
  • r.URL.Query() 取查询参数,r.Body 读请求体
  • Go 1.22 ServeMux 支持路径参数和方法匹配
  • http.Client 发请求,务必设 Timeout
  • NewRequestWithContext 实现超时取消
  • 生产环境用 Server.Shutdown 优雅关闭

下一节讲 flag 命令行解析和 log/slog 日志。