Аннотация: Содержание этой статьи очень простое и легкое для понимания.исходный адрес, я чувствую, что это самое ясноеnet/httpСтатья об использовании пакета, поэтому переведите ее и поделитесь.
Основа всего: ServeMux и обработчики
Обработка HTTP-запросов на языке Go в основном связана с двумя вещами:ServeMuxа такжеHandler.
ServrMuxПо сути, маршрутизатор HTTP-запросов (или мультиплексор). Он сравнивает входящий запрос со списком предопределенных URL-адресов и вызывает связанный обработчик, когда путь совпадает.
ОбработчикОтвечает за вывод заголовков и тела ответа HTTP. любой удовлетворенныйhttp.HandlerинтерфейсВсе объекты могут быть использованы в качестве обработчика. С точки зрения непрофессионала, если объект имеет следующую подписьServeHTTPМетод может быть:
ServeHTTP(http.ResponseWriter, *http.Request)
HTTP-пакет Go поставляется с несколькими функциями для обычных обработчиков, таких какFileServer,NotFoundHandlerа такжеRedirectHandler. Начнем с простого конкретного примера:
$ mkdir handler-example
$ cd handler-example
$ touch main.go
//File: main.go
package main
import (
"log"
"net/http"
)
func main() {
mux := http.NewServeMux()
rh := http.RedirectHandler("http://example.org", 307)
mux.Handle("/foo", rh)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
Быстро пройдитесь по коду:
-
существует
mainВ функции мы используем толькоhttp.NewServeMuxфункция для создания пустогоServeMux. -
Затем мы используем
http.RedirectHandlerФункция создает новый обработчик, который выполняет операцию перенаправления 307 для всех полученных запросов наhttp://example.org. -
Далее мы используем
ServeMux.HandleФункция регистрирует обработчик во вновь созданномServeMux, поэтому он находится в пути URL/fooВсе полученные запросы передаются этому процессору. -
Наконец мы создаем новый сервер и проходим
http.ListenAndServeФункция слушает все входящие запросы, передавая только что созданныйServeMuxчтобы соответствовать соответствующему обработчику для запроса.
Идем дальше и запускаем эту программу:
$ go run main.go
Listening...
затем посетите в своем браузереhttp://localhost:3000/foo, вы должны увидеть, что запрос был успешно перенаправлен.
Вы должны заметить некоторые интересные вещи:ListenAndServerСигнатура функцииListenAndServe(addr string, handler Handler), но второй параметр, который мы передаем, этоServeMux.
Мы можем сделать это, потому чтоServeMuxТакже естьServeHTTPметод, поэтому он также является законнымHandler.
Для меня будетServerMuxИспользование его в качестве специального обработчика является упрощением. Вместо вывода самого ответа он передает запрос другим обработчикам, зарегистрированным в нем. Поначалу это может показаться не очевидным скачком, но в Go theHandlerЦепочка вместе - очень распространенное использование.
Пользовательские обработчики
Давайте создадим собственный обработчик, который будет выводить текущее местное время в определенном формате:
type timeHandler struct {
format string
}
func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("The time is: " + tm))
}
Сам код не имеет значения в этом примере.
Суть в том, что у нас есть объект (в данном случаеtimerHandlerструктура, но это также может быть строка, функция или что-то еще), мы реализуемServeHTTP(http.ResponseWriter, *http.Request)Подписанный метод — это все, что нам нужно для создания обработчика.
Мы интегрируем это в конкретный пример:
//File: main.go
package main
import (
"log"
"net/http"
"time"
)
type timeHandler struct {
format string
}
func (th *timeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(th.format)
w.Write([]byte("The time is: " + tm))
}
func main() {
mux := http.NewServeMux()
th := &timeHandler{format: time.RFC1123}
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
mainВ функции мы инициализируем как обычную структуруtimeHandler,использовать&Символ получает свой адрес. Затем, как и в предыдущем примере, используемmux.Handleфункция, чтобы зарегистрировать его с помощьюServerMux.
Теперь, когда мы запускаем это приложение,ServerMuxлюбая пара/timeзапросить напрямуюtimeHandler.ServeHTTPобработка методом.
Посетите этот адрес, чтобы увидеть эффект:http://localhost:3000/time.
Обратите внимание, что мы можем легко повторно использовать на нескольких маршрутахtimeHandler:
func main() {
mux := http.NewServeMux()
th1123 := &timeHandler{format: time.RFC1123}
mux.Handle("/time/rfc1123", th1123)
th3339 := &timeHandler{format: time.RFC3339}
mux.Handle("/time/rfc3339", th3339)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
использовать функцию как обработчик
Для простых случаев (например, в примере выше) определите новый с помощьюServerHTTPПользовательские типы для методов немного громоздки. Давайте посмотрим на другой способ, мы используемhttp.HandlerFuncтип, чтобы обычная функция удовлетворялаHandlerУсловия интерфейса.
любой естьfunc(http.ResponseWriter, *http.Request)Знаковые функции могут быть преобразованы вHandlerFuncТипы. Это полезно, потому чтоHandlerFuncвстроенный объектServeHTTPметод, последний может быть умным и удобным для вызова содержимого функции, которую мы изначально предоставили.
Если вы все еще немного запутались, попробуйте взглянуть на [связанный исходный код]gowave.org/doublebed/pkg/net…. Вы увидите, что создание функционального объекта удовлетворяетHandlerИнтерфейс очень простой и элегантный.
Давайте повторно реализуем эту техникуtimeHandlerзаявление:
//File: main.go
package main
import (
"log"
"net/http"
"time"
)
func timeHandler(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(time.RFC1123)
w.Write([]byte("The time is: " + tm))
}
func main() {
mux := http.NewServeMux()
// Convert the timeHandler function to a HandlerFunc type
th := http.HandlerFunc(timeHandler)
// And add it to the ServeMux
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
По сути, превращение функции вHandlerFuncпосле регистрации наServeMuxявляется распространенным использованием, поэтому язык Go предоставляет удобный способ сделать это:ServerMux.HandlerFuncметод.
Переписываем с удобствомmain()Функция выглядит следующим образом:
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/time", timeHandler)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
В большинстве случаев такой способ использования функций в качестве процессоров работает хорошо. Но когда все становится сложнее, появляются некоторые ограничения.
Вы могли заметить, что, в отличие от предыдущего способа, нам пришлось жестко запрограммировать формат времени, чтобыtimeHandlerв методе. Если мы хотим отmain()Как передать некоторую информацию или переменные процессору в функции?
Элегантный способ — поместить наш обработчик в замыкание с переменными, которые мы хотим использовать в нем:
//File: main.go
package main
import (
"log"
"net/http"
"time"
)
func timeHandler(format string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
return http.HandlerFunc(fn)
}
func main() {
mux := http.NewServeMux()
th := timeHandler(time.RFC1123)
mux.Handle("/time", th)
log.Println("Listening...")
http.ListenAndServe(":3000", mux)
}
timeHandlerФункции теперь имеют более тонкую идентичность. Вместо того, чтобы оборачивать функцию как обработчик (как мы делали раньше), мы теперь используем ее для возврата обработчика. Этот механизм имеет два ключевых момента:
Первый заключается в созданииfn, которая является анонимной функцией, которая будетformatПеременные инкапсулируются в замыкание. Природа замыкания позволяет процессору в любом случае обращаться к локальной области видимости.formatПеременная.
Во-вторых, наша функция замыкания удовлетворяетfunc(http.ResponseWriter, *http.Request)подписать. Если вы помните, что мы говорили ранее, это означает, что мы можем преобразовать его вHandlerFuncтип (удовлетворенныйhttp.Handlerинтерфейс). нашtimeHandlerЗатем функция преобразуется вHandlerFuncвернуть.
В приведенном выше примере мы уже можем передать обработчику простую строку. Однако в реальных приложениях вы можете использовать этот метод для передачи соединений с базой данных, групп шаблонов или других контекстов уровня приложения. Использование глобальных переменных также является хорошим вариантом, с дополнительным преимуществом написания более элегантных автономных обработчиков для тестирования.
Возможно, вы также видели такое же написание, например:
func timeHandler(format string) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
})
}
или по возвращении используйтеHandlerFuncНеявное преобразование типов:
func timeHandler(format string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
}
Более удобный DefaultServeMux
Вы могли видеть это во многих местахDefaultServeMux, из самого простогоHello WorldНапример, в исходный код языка go.
Мне потребовалось много времени, чтобы понятьDefaultServerMuxВ этом нет ничего особенного.DefaultServerMuxэто то, что мы использовали раньшеServerMux, только следуетnet/httppКогда пакет инициализируется, он инициализируется автоматически. Соответствующие строки в исходном коде Go выглядят следующим образом:
var DefaultServeMux = NewServeMux()
net/httppackage предоставляет набор ярлыков для сопровожденияDefaultServeMux:http.Handleа такжеhttp.HandleFunc. Эти функции делают то же самое, что и функции с похожими именами, которые мы видели ранее, с той лишь разницей, что они регистрируют обработчик дляDefaultServerMux, и до того, как мы зарегистрировались, чтобы создать собственныйServeMux.
также,ListenAndServeВ случае, когда другой процессор не предусмотрен (т. е. второй параметр установлен вnil), который используется внутриDefaultServeMux.
Итак, в качестве последнего шага мы используемDefaultServeMuxпереписать нашtimeHandlerзаявление:
//File: main.go
package main
import (
"log"
"net/http"
"time"
)
func timeHandler(format string) http.Handler {
fn := func(w http.ResponseWriter, r *http.Request) {
tm := time.Now().Format(format)
w.Write([]byte("The time is: " + tm))
}
return http.HandlerFunc(fn)
}
func main() {
// Note that we skip creating the ServeMux...
var format string = time.RFC1123
th := timeHandler(format)
// We use http.Handle instead of mux.Handle...
http.Handle("/time", th)
log.Println("Listening...")
// And pass nil as the handler to ListenAndServe.
http.ListenAndServe(":3000", nil)
}