interface http.Handler
Thuộc phần Lập trình hướng đối tượng trong hành trình GO của Coddy. Bài học 77 trên 107.
Giao diện http.Handler là nền tảng của kiến trúc máy chủ web trong Go. Giao diện này xác định cách xử lý các yêu cầu HTTP, và mọi kiểu dữ liệu triển khai giao diện này đều có thể phục vụ các yêu cầu web.
Giao diện này vô cùng đơn giản:
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}Bất kỳ kiểu nào có phương thức ServeHTTP đều có thể xử lý yêu cầu HTTP. Đây là một handler tùy chỉnh:
type GreetHandler struct {
Greeting string
}
func (h GreetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "%s, visitor!", h.Greeting)
}Bạn có thể dùng bộ xử lý này trực tiếp với máy chủ:
handler := GreetHandler{Greeting: "Welcome"}
http.Handle("/greet", handler)Với những trường hợp đơn giản hơn, Go cung cấp http.HandlerFunc, một kiểu cho phép bạn chuyển một hàm thông thường thành một bộ xử lý:
func hello(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Hello!")
}
http.Handle("/hello", http.HandlerFunc(hello))Bạn sẽ thấy rõ sức mạnh của giao diện này khi nhận ra rằng các trình xử lý có thể bao bọc những trình xử lý khác. Nhờ đó, bạn có thể áp dụng các mẫu middleware để thêm chức năng ghi nhật ký, xác thực hoặc các mối quan tâm xuyên suốt khác mà không cần sửa đổi logic của trình xử lý ban đầu.
Thử thách
DễHãy cùng xây dựng một hệ thống bộ xử lý HTTP đơn giản mô phỏng cách xử lý các yêu cầu web! Vì không thể chạy máy chủ HTTP thực tế trong môi trường này, bạn sẽ tạo các bộ xử lý triển khai giao diện http.Handler và minh họa cách chúng xử lý yêu cầu bằng cách làm việc với các đối tượng yêu cầu và phản hồi giả lập.
Bạn sẽ sắp xếp code trong hai tệp:
handlers.go: Định nghĩa các bộ xử lý HTTP tùy chỉnh.Tạo một cấu trúc
MockResponseWriterđể ghi lại nội dung sẽ được ghi vào phản hồi HTTP. Cấu trúc này cần lưu phần thân phản hồi dưới dạng chuỗi và triển khai phương thứcWrite(data []byte) (int, error)để nối dữ liệu vào phần thân. Thêm phương thứcBody() stringđể lấy phần phản hồi đã tích lũy.Tạo cấu trúc
MockRequestvới các trường chuỗiPathvàMethodđể mô phỏng một yêu cầu HTTP.Bây giờ, hãy tạo hai cấu trúc bộ xử lý triển khai theo mẫu giao diện
http.Handler(có phương thứcServeHTTPnhận*MockResponseWritervà*MockRequest):WelcomeHandlercó trườngMessage, ghi thông điệp, sau đó là đường dẫn yêu cầuMethodHandler, ghi các phản hồi khác nhau tùy theo phương thức yêu cầu:"Fetching data"cho GET,"Creating resource"cho POST và"Method not supported"cho mọi trường hợp khác
main.go: Xử lý yêu cầu thông qua các bộ xử lý.Đọc loại bộ xử lý (
welcomehoặcmethod), sau đó đọc thông tin chi tiết về yêu cầu. Với bộ xử lý welcome, hãy đọc thêm thông điệp chào mừng.Tạo bộ xử lý phù hợp, tạo yêu cầu giả lập và gọi
ServeHTTPđể xử lý yêu cầu. In phần thân phản hồi ra.
Chương trình sẽ nhận các đầu vào sau:
- Dòng 1: Loại bộ xử lý (
welcomehoặcmethod) - Dòng 2: Đường dẫn yêu cầu
- Dòng 3: Phương thức yêu cầu
- Dòng 4 (chỉ với welcome): Thông điệp chào mừng
Ví dụ, với đầu vào:
welcome
/home
GET
Hello fromĐầu ra của bạn sẽ là:
Hello from /homeVà với đầu vào:
method
/api/users
POSTĐầu ra của bạn sẽ là:
Creating resourceVà với đầu vào:
method
/api/data
GETĐầu ra của bạn sẽ là:
Fetching dataVà với đầu vào:
method
/api/items
DELETEĐầu ra của bạn sẽ là:
Method not supportedThử thách này minh họa khái niệm cốt lõi của giao diện http.Handler: bất kỳ kiểu nào có phương thức ServeHTTP đều có thể xử lý yêu cầu. Trong ứng dụng thực tế, http.ResponseWriter và *http.Request đến từ thư viện chuẩn của Go, nhưng mẫu triển khai bộ xử lý vẫn hoàn toàn giống như vậy.
Tự làm thử
package main
import (
"bufio"
"fmt"
"os"
)
func main() {
scanner := bufio.NewScanner(os.Stdin)
// Đọc loại trình xử lý
scanner.Scan()
handlerType := scanner.Text()
// Đọc đường dẫn của yêu cầu
scanner.Scan()
path := scanner.Text()
// Đọc phương thức của yêu cầu
scanner.Scan()
method := scanner.Text()
// Tạo yêu cầu mô phỏng
request := &MockRequest{
Path: path,
Method: method,
}
// Tạo bộ ghi phản hồi mô phỏng
response := &MockResponseWriter{}
// TODO: Dựa vào handlerType, tạo trình xử lý phù hợp
// Nếu handlerType là "welcome", đọc thông điệp chào mừng và tạo WelcomeHandler
// Nếu handlerType là "method", tạo MethodHandler
// Gọi ServeHTTP trên trình xử lý với response và request
if handlerType == "welcome" {
scanner.Scan()
message := scanner.Text()
// TODO: Tạo WelcomeHandler với thông điệp rồi gọi ServeHTTP
_ = message // Xóa dòng này khi triển khai
} else if handlerType == "method" {
// TODO: Tạo MethodHandler rồi gọi ServeHTTP
}
// In ra nội dung phản hồi
fmt.Println(response.Body())
}
Bài học này có một bài trắc nghiệm ngắn. Bắt đầu bài học để trả lời và theo dõi tiến độ.
Tất cả bài học trong Lập trình hướng đối tượng
1Nền tảng OOP trong Go
Tệp bên ngoàiWorkspace và module trong GoPackage và importTên được export và không được exportGiới thiệu về OOP trong GoStruct như lớpĐịnh nghĩa phương thức cho structReceiver kiểu con trỏ và kiểu giá trịKhởi tạo structHàm khởi tạoÔn tập: máy tính đơn giản4Interface
Giới thiệu về interfaceTriển khai ngầm địnhInterface như một hợp đồngInterface rỗng (any)Khẳng định kiểuswitch kiểuKết hợp interfaceInterface Stringer và ErrorÔn tập: Máy tính hình học7Tính đóng gói
Trường được xuất và không được xuấtĐóng gói ở cấp packagePhương thức getter và setterẨn thông tin trong GoÔn tập: hồ sơ học sinh10Generics (Go 1.18 trở lên)
Giới thiệu về GenericsTham số kiểuRàng buộc kiểuStruct tổng quátGiải pháp thay thế cho phương thức tổng quátÔn tập: tập hợp tổng quát13Các mẫu thiết kế phần 1
Giới thiệu về mẫu thiết kếMẫu SingletonMẫu FactoryMẫu Abstract FactoryMẫu ObserverMẫu Strategy2Tìm hiểu chuyên sâu về kiểu dữ liệu và struct
Kiểu cơ bản và kiểu tổng hợpĐịnh nghĩa kiểu dữ liệu tùy chỉnhThẻ structStruct ẩn danhStruct lồng nhauGiá trị zero và giá trị mặc địnhÔn tập: sổ liên lạc5Ưu tiên kết hợp hơn kế thừa
Vì sao Go không có kế thừaNhập môn nhúng structTự động nâng phương thứcNhúng nhiều structNhúng và kết tậpChe khuất phương thức được nhúngÔn tập: hệ phân cấp nhân viên8Xử lý lỗi và lập trình hướng đối tượng
Giao diện errorCác kiểu lỗi tùy chỉnhBọc lỗi với fmt.ErrorfLỗi sentinelerrors.Is() và errors.As()Panic, defer và recoverÔn tập: trình phân tích tệp11Thư viện chuẩn và OOP
io.Reader và io.Writersort.Interfaceinterface fmt.Stringerencoding/json với structinterface http.HandlerÔn tập: mô hình REST API3Con trỏ và bộ nhớ
Cơ bản về con trỏ trong GoCon trỏ đến structTruyền theo giá trị và tham chiếuHàm new()Thu gom rác trong GoÔn tập: xây dựng danh sách liên kết6Đa hình trong Go
Đa hình qua interfaceĐịnh kiểu vịt trong GoQuy tắc thỏa mãn interfaceCác tập hợp đa hìnhTiêm phụ thuộcÔn tập: bộ xử lý thanh toán9Lập trình đồng thời và lập trình hướng đối tượng
Cơ bản về goroutineChannel và giao tiếpChannel có bộ đệm và không có bộ đệmCâu lệnh selectsync.Mutex và sync.RWMutexsync.WaitGroupThiết kế struct an toàn luồngÔn tập: worker poolTự luyện tập: Trình biên dịch Go online