Bài học theo mô-đun (5/5)
Dự án nhỏ: máy chủ HTTP
Gói stdlib net/http đủ để xây dựng một máy chủ HTTP sẵn sàng sản xuất: định tuyến, trình xử lý, phần mềm trung gian, tắt máy một cách duyên dáng. Không cần khung. Đối với các API REST điển hình thì đây là nền tảng.
Xin chào thế giới
package main
import (
"fmt"
"net/http"
)
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "hello %s", r.URL.Path[1:])
})
if err := http.ListenAndServe(":8080", nil); err != nil {
panic(err)
}
}http.ListenAndServe chặn và chỉ trả về khi có lỗi. nil làm đối số thứ hai = "sử dụng DefaultServeMux".
Chữ ký xử lý
Tất cả các trình xử lý đều có chung chữ ký:
func(w http.ResponseWriter, r *http.Request)w http.ResponseWriter— đầu ra: tiêu đề, mã trạng thái, nội dung.r *http.Request— đầu vào: phương thức, URL, tiêu đề, nội dung, ngữ cảnh.
Mẫu điển hình:
func ping(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK) // optional, defaults to 200
fmt.Fprintln(w, `{"ok":true}`)
}Định tuyến bằng ServeMux
*http.ServeMux là bộ định tuyến cơ sở. Vì Go 1.22 nó hỗ trợ các mẫu có tham số phương thức và đường dẫn:
mux := http.NewServeMux()
mux.HandleFunc("GET /users/{id}", func(w http.ResponseWriter, r *http.Request) {
id := r.PathValue("id")
fmt.Fprintln(w, "user", id)
})
mux.HandleFunc("POST /users", createUser)
http.ListenAndServe(":8080", mux)Đối với các bộ định tuyến phức tạp hơn (nhóm, phần mềm trung gian tích hợp, biểu thức chính quy), có chi, gorilla/mux, gin, echo. stdlib bao gồm 90% trường hợp.
Middleware như một vật trang trí
Phần mềm trung gian là một hàm func(http.Handler) http.Handler:
func logMW(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
next.ServeHTTP(w, r)
log.Printf("%s %s %s", r.Method, r.URL.Path, time.Since(start))
})
}
http.ListenAndServe(":8080", logMW(mux))Để xâu chuỗi chúng: logMW(authMW(rateLimitMW(mux))). Mỗi phần mềm trung gian trang trí phần tiếp theo.
http.Server để kiểm soát tốt
http.ListenAndServe(addr, h) là lối tắt. Để hết thời gian chờ, TLS và tắt máy nhẹ nhàng, bạn cần khởi tạo http.Server:
srv := &http.Server{
Addr: ":8080",
Handler: mux,
ReadHeaderTimeout: 5 * time.Second,
ReadTimeout: 30 * time.Second,
WriteTimeout: 30 * time.Second,
IdleTimeout: 2 * time.Minute,
}
log.Fatal(srv.ListenAndServe())Tắt máy duyên dáng với bối cảnh
Khi quá trình nhận được SIGINT/SIGTERM, bạn muốn hoàn tất các yêu cầu đang thực hiện trước khi đóng:
ctx, stop := signal.NotifyContext(context.Background(),
os.Interrupt, syscall.SIGTERM)
defer stop()
srv := &http.Server{Addr: ":8080", Handler: mux}
go func() {
if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatal(err)
}
}()
<-ctx.Done() // wait for the signal
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()
if err := srv.Shutdown(shutdownCtx); err != nil {
log.Println("shutdown:", err)
}srv.Shutdown ngừng chấp nhận các kết nối mới và chờ các kết nối đang hoạt động kết thúc (đến thời hạn đã qua).
Đọc nội dung JSON
type CreateUserReq struct {
Name string `json:"name"`
}
func createUser(w http.ResponseWriter, r *http.Request) {
var req CreateUserReq
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
// ... use req.Name
w.WriteHeader(http.StatusCreated)
}r.Body là io.ReadCloser: máy chủ tự động đóng nó nhưng json.NewDecoder không đọc qua đối tượng JSON đầu tiên (hữu ích cho việc xác thực dữ liệu đầu vào).
Bài tập
Đăng ký l'handler xin chào tuyến đường sulla '/' sử dụng http.HandleFunc.
Giải pháp khả dụng sau 3 lần thử
Avvia il server su :8080 với http.ListenAndServe và đóng dấu lỗi nếu không phải là không.
Giải pháp khả dụng sau 3 lần thử
Vậy công ty có nên sử dụng thành ngữ để xử lý HTTP trong Go không?
func H(???) { ... }