Аннотация: Содержание этой статьи очень простое и легкое для понимания.исходный адрес, я чувствую, что это самое ясное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/http
package предоставляет набор ярлыков для сопровождения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)
}