golang; проблемы с пониманием функции в качестве приемника - PullRequest
3 голосов
/ 06 февраля 2020

Я пытаюсь прочитать это: https://blog.golang.org/error-handling-and-go конкретно раздел под названием Simplifying repetitive error handling.

Они называют http.Handle так:

func init() {
    http.Handle("/view", appHandler(viewRecord))
}
* Второй аргумент 1010 *http.Handle ожидает тип Handler (https://golang.org/pkg/net/http/#Handler), который должен иметь метод serveHttp.

Функция serveHttp здесь:

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if err := fn(w, r); err != nil {
        http.Error(w, err.Error(), 500)
    }
}

Итак, их тип appHandler теперь реализует интерфейс обработчика, потому что он реализует ServeHTTP, я понял. Поэтому его можно использовать в функции Handle, тогда как viewRecord - нет.

Я запутался в отношениях между viewRecord типа appHandler и ServeHTTP. Какие звонки какие? Они пишут в скобках комментарий о том, что «функция тоже может быть получателем», и я думаю, что именно здесь меня и запутали.

Здесь, с fn appHandler в качестве получателя, я ожидал бы что-то вроде viewRecord.serveHTTP(), но это не имеет смысла, а viewRecord - это функция. Я думаю, что происходит то, что функция Handle вызывает serveHTTP, но как serveHTTP вызывает viewRecord?

Также appHandler(viewRecord) выполняет приведение?

В основном я ищу некоторую ясность относительно того, что означает для функция быть получателем . Я новичок - я sh до go, и я думаю, что случайно приземлился на нетривиальной мине здесь.

Ответы [ 2 ]

2 голосов
/ 06 февраля 2020

Любой тип может быть получателем. Например:

type X int

Здесь X - это новый тип, и вы можете создать для него методы:

func (x X) method() {
  // Do something with x
}

В Go функции похожи на любой другой тип , Поэтому, если у вас есть тип функции:

type F func()

Здесь F - это новый тип, поэтому вы можете определить методы для него:

func (x F) method() {
   x()
}

С помощью вышеуказанного объявления, теперь Вы можете вызвать value.method(), если value имеет тип F.

a:=F(func() {fmt.Println("hey")})
a.method()

Здесь a является переменной типа F. F имеет метод с именем method, поэтому вы можете вызвать a.method. Когда вы вызываете это, a.method вызывает a, что является функцией.

Возвращаясь к вашему примеру, appHandler представляется типом функции:

type appHandler func(http.ResponseWriter, *http.Request)

Так любая функция с этой подписью может использоваться вместо appHandler. Допустим, вы пишете такую ​​функцию:

func myHandler(http.ResponseWriter, *http.Request) {
  // Handle request
}

Вы можете передавать эту функцию везде, где запрашивается appHandler. Тем не менее, вы не можете передать, где требуется Handler, не написав такую ​​структуру:

type myHandlerStruct struct{}

func (myHandlerStruct) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   myHandler(w,r)
}

Вместо определения новой структуры вы можете определить метод для типа appHandler:

func (a appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
   a(w,r)
}

Теперь вы можете передать appHandler туда, где требуется appHandler, а также туда, где требуется Handler. Если он вызывается как Handler, метод ServeHTTP просто переадресует вызов базовой функции.

2 голосов
/ 06 февраля 2020

ОК, там много вопросов, но я сделаю все возможное, чтобы охватить все это.

Это определяет метод ServeHTTP для типа appHandler, что означает, что appHandler теперь является допустимым net/http.Handler. Тип appHandler является типом функции, так что да - здесь у вас есть значение функции с методами, которые вы можете вызывать из него, отдельно от вызова самой функции. Кстати, именно так http.HandleFunc работает в стандартной библиотеке - проверка его исходного кода также может помочь понять, как это работает.

Когда обработчик зарегистрирован, net/http вызывает его метод ServeHTTP обрабатывать входящие запросы. Этот метод для нашего appHandler типа:

func (fn appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    if err := fn(w, r); err != nil {
        http.Error(w, err.Error(), 500)
    }
}

Итак, в первой строке функции ServeHTTP вызывает функцию appHandler - fn appHandler - наш получатель, что делает fn значение функции, которое мы можем вызвать:

fn(w, r)

Это используется путем преобразования нашей функции-обработчика в тип appHandler:

http.Handle("/view", appHandler(viewRecord))

Это на самом деле не отличается от более распространенный шаблон промежуточного программного обеспечения:

func middleware(fn func(w http.ResponseWriter, r *http.Request) error) func(w http.ResponseWriter, r *http.Request) {
    return func(w http.ResponseWriter, r *http.Request) {
        if err := fn(w, r); err != nil {
            http.Error(w, err.Error(), 500)
        }
    }
}

Это делает то же самое, но с использованием параметра замыкания и функции вместо получателя метода и функции. Это используется при вызове нашей функции-обертки:

http.Handle("/view", http.HandlerFunc(middleware(viewRecord)))

Используется наша функция middleware, чтобы обернуть viewRecord и преобразовать ее в http.HandlerFunc (которая, как упоминалось ранее, фактически сделает то же самое appHandler делал с методом типа функции).

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...