Go тілі: нөлден өз блогыңызға дейін 21-сабақ (барлығы 50)
Өңдеуші және middleware: барлық бағытқа бір орама
Go курсының жиырма бірінші сабағы. Бағыттауыштың айналасындағы орама әр сұраныс үшін бірдей істі істейді: журнал, жауап уақыты, тексеру. http.Handler деген не, орама неге өңдеушіні алып, өңдеуші қайтарады және ResponseWriter сақтамайтын жауап кодын қалай білуге болады.
Не үшін керек
Журнал әр бетте керек. Кіру тексерісі — жартысында. Уақытты өлшеу — бәрінде. Үш жолды әр өңдеушіге көшіру деген — бір күні біреуінде ұмытып, «неге бұл бет журналға жазылмайды» деп жарты күн іздеу деген сөз.
Мұндай нәрсені бір рет жазып, бәріне бірден кигізеді.
Бірден тұтас
Жаңа қалта, go mod init sabaq19, main.go:
package main
import (
"fmt"
"log"
"net/http"
"time"
)
// logging кез келген өңдеушіні орап, журналға бір жол жазады.
func logging(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
start := time.Now()
rec := &recorder{ResponseWriter: w, code: http.StatusOK}
next.ServeHTTP(rec, r)
log.Printf("%s %s → %d, %s ішінде", r.Method, r.URL.Path, rec.code, time.Since(start).Round(time.Millisecond))
})
}
// recorder жауап кодын есте сақтайды: ResponseWriter оны сақтамайды.
type recorder struct {
http.ResponseWriter
code int
}
func (rec *recorder) WriteHeader(code int) {
rec.code = code
rec.ResponseWriter.WriteHeader(code)
}
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "басты бет")
}
func slow(w http.ResponseWriter, r *http.Request) {
time.Sleep(120 * time.Millisecond)
fmt.Fprintln(w, "баяу бет")
}
func gone(w http.ResponseWriter, r *http.Request) {
http.Error(w, "жоқ", http.StatusNotFound)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("GET /{$}", home)
mux.HandleFunc("GET /slow", slow)
mux.HandleFunc("GET /gone", gone)
log.Println("http://localhost:8080")
log.Fatal(http.ListenAndServe(":8080", logging(mux)))
}
Іске қосып, төрт мекенжайға да барыңыз — /, /slow, /gone және жоқ /kokek. Төменге қарамас бұрын — соңғысы журналға түсе ме, дауыстап айтыңыз. Содан кейін терминалмен салыстырыңыз:
GET / → 200, 0s ішінде
GET /slow → 200, 121ms ішінде
GET /gone → 404, 0s ішінде
GET /kokek → 404, 0s ішінде
Соңғы жол — маңыздысы. /kokek бірде-бір өңдеушіңізге жеткен жоқ, 404-ті бағыттауыштың өзі берді, — ал журналда жазба бар.
Талдау
http.Handler — бір әдісті интерфейс
type Handler interface {
ServeHTTP(w http.ResponseWriter, r *http.Request)
}
ServeHTTP-ті білетіннің бәрі — өңдеуші. ServeMux те өңдеуші: онда осы әдіс бар, ол жолға қарап, керекті функцияны шақырады.
Сабақтың бүкіл құрылысы осыдан. Бағыттауыш өңдеуші болса, оны өңдеуші күтетін жерге беруге болады. Мысалы — басқа өңдеушіге.
HandlerFunc — функциядан интерфейске көпір
Сіздің home мен slow — кәдімгі функциялар, оларда ServeHTTP әдісі жоқ. Оларды http.HandlerFunc айналдырады: бұл — жалғыз әдісі бар тип, ол функцияның өзін шақыра салады.
http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ... })
Жазудан қорықпаңыз: бұл int64(n) сияқты түрді түрлендіру. Функцияны функция етіп жариялайды, ал интерфейске осы жолмен кигізеді.
Middleware — өңдеушіні алып, өңдеуші қайтаратын функция
func logging(next http.Handler) http.Handler
Бүкіл мәні қолтаңбада. Кірісте — бұрынғысы. Шығыста — сол баяғысы, тек орамада. Сондықтан орамаларды бірінің үстіне бірін кигізуге болады, ал бағыттауыш та, өңдеушілер де олар туралы білмейді.
Ораманың ішінде үш бөлік бар: алдында бірдеңе, next.ServeHTTP(w, r) шақыруы, содан кейін бірдеңе.
Елестетіп көріңіз. Ғимарат кіреберісіндегі турникет. Ол сіздің қай кабинетке бара жатқаныңызды білмейді және сіздің орныңызға шешім қабылдамайды. Ол кірген уақытты белгілейді, өткізеді және шыққан уақытты белгілейді — бәріне бірдей.
Рет: орамалар матрёшкадай кигізіледі
Екеуін кигізіп, қай ретпен істейтінін көрейік:
http.ListenAndServe(":8080", mark("сыртқы")(mark("ішкі")(mux)))
сыртқы кірді
ішкі кірді
өңдеуші
ішкі шықты
сыртқы шықты
Сыртқысы сұранысты бірінші қарсы алады, жауапты соңғы шығарып салады. Осыдан қарапайым ереже: бәрінен бұрын болуға тиіс нәрсені соңғы, сырттан кигізеді.
Елестетіп көріңіз. Матрёшкалар. Сыртқысын алдыңыз — астында келесісі, ең кішкентайына дейін солай; кері жинағанда рет керісінше. Сұраныс олардың бәрінен өтіп барады да, сол жолмен қайтады.
Жауап кодын өзіңіз есте сақтауға тура келеді
Жаңадан бастағандар сүрінетін жер осы. ResponseWriter-де қандай код жіберілгенін сұрайтын тәсіл жоқ. Ол тек жаза алады.
Сондықтан орама өзінікін ұсынады:
type recorder struct {
http.ResponseWriter
code int
}
func (rec *recorder) WriteHeader(code int) {
rec.code = code
rec.ResponseWriter.WriteHeader(code)
}
Құрылымның бірінші жолы — ендіру: recorder ResponseWriter-дің барлық әдісін тегін алады және толыққанды ResponseWriter болып қалады. Өзінікі біреу ғана — WriteHeader, ол кодты есте сақтап, әрі қарай береді.
Ескерту. Бұл
recorder— оқуға арналған. Нағызы көбірек біледі: кодты екі рет жазуға жол бермейді (Go-да екіншіWriteHeaderүнсіз еленбейді, ал мұны білген пайдалы) жәнеResponseWriter-дің қосымша қабілеттерін әрі қарай береді — мысалы, ағынмен беру істемейтінhttp.Flusher-ді. Журналға біздікі жетеді; кітапханаға — жоқ.
Ал жасау кезіндегі code: http.StatusOK — әшекей емес: денені жаза салатын өңдеуші WriteHeader-ді мүлде шақырмайды, ал жауап бәрібір 200 кодымен кетеді.
Елестетіп көріңіз. Бланк астындағы көшірме қағаз. Түпнұсқа алушыға кетеді, көшірмесі сізде қалады — әрі алушының алғанында ештеңе өзгермейді.
Барлық бағытқа орама және біреуіне ғана орама
http.ListenAndServe(":8080", logging(mux)) // бәріне
mux.Handle("GET /admin", onlyStaff(adminPage)) // бір бағытқа
Біріншісі — сайтқа ортақ: журнал, уақыт, panicнен кейін қалпына келу. Екіншісі — бәріне керек емесі: кіру тексерісі, сұраныс жиілігін шектеу.
Орамаға не салмау керек
Орама азғырады: ол бәрін көреді. Сондықтан онда орны жоқ нәрселер тез пайда болады.
Ережесі қарапайым: орама бәріне бірдей істі істейді. Ішінде if r.URL.Path == … пайда болды дегенше, бұл орама емес, екінші рет әрі нашарлау жазылған бағыттауыш. Ондай жер — не бөлек өңдеуші, не бір бағытқа арналған орама.
Сабақ картасы
Өз сөзіңізбен айтыңыз
Қарамай, дауыстап немесе қағазға жазып жауап беріңіз. Жауаптары — сабақтың соңында.
muxфункция болмаса да,loggingоны неге орай алады?- Екі орама кигізіңіз. Сұранысты қайсысы бірінші көреді?
- Жауап коды
wарқылы жіберіліп қойса, орамаға өзrecorder-і не үшін керек?
Тапсырма
Міндетті. Әр жауапқа X-Blog: go-oqu тақырыбын қосатын withHeader орамасын жазыңыз да, оны logging-пен бірге бүкіл бағыттауышқа кигізіңіз. curl -i арқылы тақырыптың үш мекенжайда да, 404-те де бар екенін тексеріңіз. Содан кейін алдында және соңында хабар шығаратын екінші орама жазып, реттің дәл болжағаныңыздай екеніне көз жеткізіңіз.
Қалауыңызша.
recorderішіне жауап байттарын санауды қосып, оны журналға басып шығарыңыз.- Өңдеушінің panicін ұстап, байланысты үзудің орнына 500 беретін
recoverорамасын жазыңыз. - Тек
/slowүшін орама жасаңыз —mux.Handleарқылы — де, басқа мекенжайларда ол істемейтініне көз жеткізіңіз.
Блогта қайда тұрады
Журналды жаңа ғана жаздыңыз — ол курстың соңына дейін қалады. Оған panicнен кейін қалпына келу және пайдаланушылар пайда болғанда кіру тексерісі қосылады.
Ал келесі сабақта жауаптың денесіне ақыры HTML кетеді: үлгілер []Article-ды бетке айналдырады, ал fmt.Fprintln өңдеушілерден кетеді.
Жауаптар
Жауаптарды көрсету
- Себебі орама функцияны емес,
http.Handlerинтерфейсін қабылдайды —ServeHTTPәдісі бар нәрсенің бәрін.ServeMux-та ол бар, сондықтан бағыттауыш та кез келгені сияқты өңдеуші. - Сыртқысы: ол сұранысты бірінші алады, жауапты соңғы береді. Рет матрёшкадағыдай — сырттан кигізілгені бірінші қарсы алады.
- Себебі
ResponseWriterтек жаза алады, одан жіберілген кодты оқу мүмкін емес. Орама өзініңrecorder-ін қояды, ол кодты жол-жөнекей есте сақтап, нағыз жазушыға өзгеріссіз береді.
Пікірлер (0)
Пікір қалдыру үшін кіріңіз →
Әзірге пікір жоқ. Бірінші болыңыз.