Поскольку почти все может иметь прикрепленные методы, почти все может удовлетворить интерфейс. Один иллюстративный пример находится в http
пакете, который определяет интерфейс Handler
. Любой объект, который реализует Handler
, может обслуживать HTTP-запросы.
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
ResponseWriter
сам по себе является интерфейсом, обеспечивающим доступ к методам, необходимым для возврата ответа клиенту. Эти методы включают стандартный метод Write
, поэтому http.ResponseWriter
можно использовать везде, где io.Writer
может быть использован. Request
- это структура, содержащая проанализированное представление запроса от клиента.
Для краткости давайте проигнорируем POST и предположим, что HTTP-запросы всегда GET; это упрощение не влияет на то, как обработчики настраиваются. Вот тривиальная, но полная реализация обработчика для подсчета, сколько раз страница посещена.
// Простой счетчик сервера.
type Counter struct {
n int
}
func (ctr *Counter) ServeHTTP(w http.ResponseWriter,
req *http.Request) {
ctr.n++
fmt.Fprintf(w, "counter = %d\n", ctr.n)
}
(Продолжая тему печати, обратите внимание, как Fprintf
может печатать на http.ResponseWriter
.) Для справки, вот как подключить такой сервер к узлу в дереве URL.
import "net/http"
...
ctr := new(Counter)
http.Handle("/counter", ctr)
Но зачем делать Counter
структурой? Целое число - это все, что нужно. (Получатель должен быть указателем, чтобы приращение было видно вызывающей стороне.)
// Упрощенный счетчик сервера.
type Counter int
func (ctr *Counter) ServeHTTP(w http.ResponseWriter,
req *http.Request) {
*ctr++
fmt.Fprintf(w, "counter = %d\n", *ctr)
}
Что делать, если ваша программа имеет некоторое внутреннее состояние, которое необходимо уведомить о том, что страница была посещена? Свяжите канал с веб-страницей.
// Канал, который отправляет оповещение на каждый визит.
// (Вероятно вы звхотите, чтобы канал был буферизированным)
type Chan chan *http.Request
func (ch Chan) ServeHTTP(w http.ResponseWriter,
req *http.Request) {
ch <- req
fmt.Fprint(w, "notification sent")
}
Наконец, скажем, мы хотели представить в /args
аргументы используемые при вызове бинарного файла сервера. Легко написать функцию для вывода аргументов.
func ArgServer() {
fmt.Println(os.Args)
}
Как мы превращаем это в HTTP-сервер? Мы могли бы сделать ArgServer
методом некоторого типа, значение которого мы игнорируем, но есть более чистый способ. Поскольку мы можем определить метод для любого типа, кроме указателей и интерфейсов, мы можем написать метод для функции. Пакет http
содержит этот код:
// Тип HandlerFunc - это адаптер, позволяющий использовать
// обычные функции как обработчики HTTP.
// Если f является функцией
// с соответствующей подписью, HandlerFunc(f) является
// Handler объекта, который вызывает f.
type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP вызывает f(w, req).
func (f HandlerFunc) ServeHTTP(w ResponseWriter,
req *Request) {
f(w, req)
}
HandlerFunc
- это тип с методом, ServeHTTP
, поэтому значения этого типа могут обслуживать HTTP-запросы. Посмотрите на реализацию метода: получатель - это функция, f
, и метод вызывает f
. Это может показаться странным, но это не так уж отличается от, скажем, случая когда приемник является каналом, а метод отправляется по каналу.
Чтобы превратить ArgServer
в HTTP-сервер, мы сначала изменим его, чтобы он имел правильную сигнатуру.
// Argument сервер.
func ArgServer(w http.ResponseWriter, req *http.Request) {
fmt.Fprintln(w, os.Args)
}
ArgServer
теперь имеет ту же сигнатуру, что и HandlerFunc
, поэтому он может быть преобразован в этот тип для доступа к его методам, как мы конвертировали Sequence
в IntSlice
для доступа к IntSlice.Sort
. Код для установки сервера лаконичен:
http.Handle("/args", http.HandlerFunc(ArgServer))
Когда кто-то посещает страницу /args
, обработчик, установленный на этой странице, имеет значение ArgServer
и тип HandlerFunc
. HTTP-сервер будет вызывать метод ServeHTTP
этого типа, с ArgServer
в качестве получателя, который в свою очередь вызовет ArgServer
(с помощью вызова f(w, req)
внутри HandlerFunc.ServeHTTP
). Аргументы будут отображены.
В этом разделе мы сделали HTTP-сервер из структуры, целого числа, канала и функции, все потому, что интерфейсы - это просто наборы методов, которые могут быть определены для (почти) любого типа.
Читайте также:
Комментариев нет:
Отправить комментарий