Middleware үндэс
Та ресторан барьж байна гэж төсөөл. Хаалгаар орж ирсэн хүн бүр эхлээд хүлээн авагч дээр зогсоно, дараа нь ширээнд суугаад захиалга өгнө. Хүлээн авагч нь бүх хэрэглэгчдэд нийтлэг үйлчилгээ үзүүлдэг — энэ яг middleware-тэй адил. Go-д middleware нь HTTP хүсэлт handler-т хүрэхээс өмнө болон хойно нэмэлт логик гүйцэтгэх функц юм.
Middleware гэж юу вэ?
Go-д middleware бол http.Handler-ийг хүлээн авч шинэ http.Handler буцаадаг функц юм. Дараах загварыг санаж ав:
package main
import (
"fmt"
"net/http"
"time"
)
// Middleware загвар: handler хүлээн авч, handler буцаана
func логгMiddleware(дараачийн http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
эхлэл := time.Now()
// Хүсэлтийн өмнө
fmt.Printf("[%s] %s %s\n", time.Now().Format("15:04:05"), r.Method, r.URL.Path)
// Дараачийн handler-ийг дуудна
дараачийн.ServeHTTP(w, r)
// Хүсэлтийн дараа
fmt.Printf("Хугацаа: %v\n", time.Since(эхлэл))
})
}
func нүүрHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Нүүр хуудас")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /", нүүрHandler)
// Middleware-г handler дээр "ороох" — wrapping
http.ListenAndServe(":8080", логгMiddleware(mux))
}
Хүсэлт ирэхэд: логгMiddleware → нүүрHandler гэсэн дарааллаар ажиллана. Middleware нь хүсэлтийн өмнө ба хойно хоёуланд код ажиллуулж чадна — энэ нь маш хүчтэй!
Authentication middleware
Бодит программд зарим route-д нэвтэрсэн хэрэглэгч л хандаж чадна. Authentication middleware нь үүнийг шалгадаг:
package main
import (
"fmt"
"net/http"
)
// Authentication шалгах middleware
func authMiddleware(дараачийн http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
// Token байхгүй бол 401 алдаа буцаана
http.Error(w, "Нэвтрэх шаардлагатай", http.StatusUnauthorized)
return // ← Чухал! дараачийн handler руу очихгүй
}
// Token байвал үргэлжлүүлнэ
// Бодит программд энд token-г шалгаж, хэрэглэгчийн мэдээллийг context-т хийнэ
fmt.Printf("Token: %s\n", token)
дараачийн.ServeHTTP(w, r)
})
}
func профайлHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Таны профайл мэдээлэл")
}
func main() {
mux := http.NewServeMux()
// Зөвхөн /profile route-д auth шаардана
мэдээлэлHandler := authMiddleware(http.HandlerFunc(профайлHandler))
mux.Handle("GET /profile", мэдээлэлHandler)
// / route auth шаарддаггүй
mux.HandleFunc("GET /", func(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Нийтийн нүүр хуудас")
})
http.ListenAndServe(":8080", mux)
}
return хийснээр middleware дараагийн handler-ийг дуудахгүй зогсоно — энэ нь authentication-д маш чухал.
Middleware-г дараалуулах — chaining
Нэг route-д олон middleware хэрэгтэй болдог: эхлээд логг, дараа нь auth, дараа нь handler. Энэ загварыг middleware chain гэнэ:
package main
import (
"fmt"
"net/http"
"time"
)
func логгMiddleware(дараачийн http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
fmt.Printf("→ %s %s\n", r.Method, r.URL.Path)
дараачийн.ServeHTTP(w, r)
fmt.Printf("← Дууслаа\n")
})
}
func хугацааMiddleware(дараачийн http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
эхлэл := time.Now()
дараачийн.ServeHTTP(w, r)
fmt.Printf("Хугацаа: %v\n", time.Since(эхлэл))
})
}
func corsMiddleware(дараачийн http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// CORS header нэмэх
w.Header().Set("Access-Control-Allow-Origin", "*")
w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE")
дараачийн.ServeHTTP(w, r)
})
}
// Chain хийх туслах функц
func chain(h http.Handler, middlewares ...func(http.Handler) http.Handler) http.Handler {
// Эцсээс нь эхлэн ороох
for i := len(middlewares) - 1; i >= 0; i-- {
h = middlewares[i](h)
}
return h
}
func гэрHandler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Амжилттай!")
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /", гэрHandler)
// cors → логг → хугацаа → handler гэсэн дараалал
handler := chain(mux, corsMiddleware, логгMiddleware, хугацааMiddleware)
fmt.Println("Server :8080 дээр ажиллаж байна...")
http.ListenAndServe(":8080", handler)
}
chain() функц middleware-уудыг зөв дарааллаар ороох боломж олгодог. Ингэснээр код уншихад хялбар болно.
Context ашиглан мэдээлэл дамжуулах
Middleware-ээс handler руу мэдээлэл дамжуулахад context ашигладаг. Жишээ нь auth middleware хэрэглэгчийн ID-г context-т хийж, handler уншдаг:
type contextТүлхүүр string
const хэрэглэгчIDТүлхүүр contextТүлхүүр = "userID"
func authMiddleware(дараачийн http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := r.Header.Get("Authorization")
if token == "" {
http.Error(w, "Нэвтрэх шаардлагатай", http.StatusUnauthorized)
return
}
// Token-оос хэрэглэгчийн ID олж, context-т хийнэ
хэрэглэгчID := "user-123" // бодит тохиолдолд token задлаж авна
ctx := context.WithValue(r.Context(), хэрэглэгчIDТүлхүүр, хэрэглэгчID)
// Шинэ context-тэй хүсэлтийг дамжуулна
дараачийн.ServeHTTP(w, r.WithContext(ctx))
})
}
func профайлHandler(w http.ResponseWriter, r *http.Request) {
// Context-оос хэрэглэгчийн ID авна
id := r.Context().Value(хэрэглэгчIDТүлхүүр).(string)
fmt.Fprintf(w, "Сайн уу, %s!\n", id)
}
Дараагийн хичээлд:
Одоо бид HTTP server болон middleware-ийг мэдэж авсан. Дараагийн хичээлд Go программ database-тэй хэрхэн харилцдагийг судална. database/sql package нь Go-ийн стандарт хэрэгсэл бөгөөд PostgreSQL, MySQL, SQLite зэрэг бүх relational database-тэй ажиллах боломжийг олгодог.