ChatGPT解决这个技术问题 Extra ChatGPT

Difference between http.Handle and http.HandleFunc?

go

The Go docs have the following example for the http package:

http.Handle("/foo", fooHandler)
http.HandleFunc("/bar", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %q", html.EscapeString(r.URL.Path))
})

I'm having sort of a difficulty understanding the difference between Handle and HandleFunc and why two are needed. Can somebody try to explain to a new Gopher in clear words?

http.Handler (what http.Handle accepts) is an interface; http.HandlerFunc (what HandleFunc accepts) is a simple type that satisfies http.Handler. For example, I have appHandler type func (w http.ResponseWriter, r *http.Request) appError and it has a ServeHTTP method that satisfies http.Handler. In my router I can mix Handle/HandleFunc as needed, as some routes might only need a basic HandlerFunc and others my custom appHandler.
I guess the answer is "No".

T
Telemachus

Basically, the HTTP server's "mux" has a map of path -> handler interface

Interfaces are used here, I assume, to allow you to implement complex path handlers that have state.

For example the file server from the standard package is a struct that contains the root dir for file service and implements the handler interface.

That said, for simple stuff, a func is easier and more clear. So they added a special generator so you can easily pass in a func.

Take a look at: server.go

from line: 1216 (as of today)

  1216  type HandlerFunc func(ResponseWriter, *Request)
  1217  
  1218  // ServeHTTP calls f(w, r).
  1219  func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
  1220      f(w, r)
  1221  }

What they are doing is implementing the interface on a custom type (which happens to match the api of the interface) that just calls itself.


Just to add a bit, it's essentially an overload (which Go doesn't allow). One method takes a func, the other takes an instance that implements the Handler interface. As David pointed out, the func parameter just gets wrapped to make it implement the Handler interface, so it's convenience.
m
merlindru

In simple terms:

Problem: I want to create an object (type) that responds to HTTP requests.

Solution: Use http.Handle for that. It accepts an http.Handler as the second argument. http.Handler is an interface and should implement ServeHTTP from the http package.

Problem: I want a function to respond to my HTTP request.

Solution: Use http.HandleFunc for that. It accepts an http.HandlerFunc as the second argument.

http.HandlerFunc is a function type and should implement ServeHTTP from the http package.


m
merlindru

No, it's different. Let's examine:

func Handle(pattern string, handler Handler) {
    DefaultServeMux.Handle(pattern, handler) 
}

Handle expects us to pass a Handler. Handler is an interface

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}

if any type implements ServeHTTP(ResponseWriter, *Request) for example: myCustomHandler then we can pass it like Handle(pattern string, myCustomHandler).

In the second scenario:

HandleFunc(pattern string, func(w ResponseWriter, r *Request) {
    // do some stuff
}

HandleFunc expects a function where Handle expects a Handler interface.

So, if you just want to pass a function then you can use http.HandleFunc(..). Like @David showed that behind the scenes it implements Handler interface by calling ServeHTTP.

type HandlerFunc func(ResponseWriter, *Request)
    
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
    f(w, r)
}

I believe the question was really around why would one choose X over Y when there are two ways of doing the same thing. It's pretty clear by just looking at two approaches that method signatures are different (this is what you are also explaining) but this answer does not provide any advice on when to choose function over struct to a beginner.
S
Samir Kape

Handler functions are merely convenient ways of creating handlers.

While both of them can be used to create handlers, but because, using handler functions is cleaner and it does the job just as well, why use handlers at all? It all boils down to design. If you have an existing interface or if you want a type that can also be used as a handler, simply add a ServeHTTP method to that interface and you’ll get a handler that you can assign to a URL. It can also allow you to build web applications that are more modular.

Using Handle

package main

import (
    "fmt"
    "net/http"
)

type HelloHandler struct{}

func (h *HelloHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello!")
}

type WorldHandler struct{}

func (h *WorldHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "World!")
}
func main() {
    hello := HelloHandler{}
    world := WorldHandler{}
    http.Handle("/hello", &hello)
    http.Handle("/world", &world)
    http.ListenAndServe(":8080", nil)
}

Using HandleFunc

package main

import (
    "fmt"
    "net/http"
)

func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello!")
}
func world(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "World!")
}
func main() {
    http.HandleFunc("/hello", hello)
    http.HandleFunc("/world", world)
    http.ListenAndServe(":8080", nil)
}

Additional information:

http.Handler is an interface with method ServeHTTP(),

// net/http/server.go

type Handler interface {
     ServeHTTP(ResponseWriter, *Request)
}

And here's a ServeHTTP information,

// net/http/server.go

ServeHTTP(w http.ResponseWriter, r *http.Request)

// where,
// http.ResponseWriter is a writer interface, and,
// http.Request is a structure with request details.

Now lets look at HandlerFunc,

// net/http/server.go
// The HandlerFunc type is an adapter to allow the use of
// ordinary functions as HTTP handlers. If f is a function
// with the appropriate signature, HandlerFunc(f) is a
// Handler that calls f.

type HandlerFunc func(ResponseWriter, *Request)
    
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request){
     f(w, r)
}

That means, http.HandlerFunc is a type that has ServeHTTP method implemented.

http.HandlerFunc(someFunc) 

// where,

// 1. someFunc() must have a signature, 
func someFunc(w http.ResponseWriter, r *http.Request)

// 2. That means, http.HandlerFunc(someFunc) is just a type casting of type http.HandlerFunc on a someFunc() and not a function call.

Now lets go to the http.Handle(),

// net/http/server.go
// Handle registers the handler for the given pattern
// in the DefaultServeMux.
// The documentation for ServeMux explains how patterns are matched.

func Handle(pattern string, handler Handler) { 
     DefaultServeMux.Handle(pattern, handler) 
}

By looking at above snippet, you may have noticed that, 2nd argument accepts a Handler interface, that means, you can create any type and implement a ServeHTTP() method for it to satisfy this. Refer below example for proof.

type MyHandler struct{}

func (h *MyHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 
    fmt.Fprintf(w, "Hello World!")
}

func main() {
    handler := MyHandler{}
    http.Handle("/hello", &handler)
    http.ListenAndServe()
}

How to wrap other function over HandleFunc? I want to have a function to authorize the request first
You might want to write an authentication middleware which will be called before your actual request handler. Refer this for more information: alexedwards.net/blog/making-and-using-middleware
Jwt authentication middleware: hackernoon.com/…