An HTTP endpoint gives a client a place to send a request and a method that determines how the request is handled. The URL routes the request; its body, headers, and method provide information the handler can use. In Go, a handler receives an http.ResponseWriter and *http.Request, then writes a response and status code. section 8.6
A small endpoint can be built and checked in stages:
- Write a handler for one action: GET can return “hello”; POST can read a body and return a created response. section 8.6
- Register a path on http.ServeMux and dispatch by request method. The /hello route sends GET and POST to separate handlers and rejects other methods. section 8.6
- Test a handler directly with httptest.NewRecorder, then use httptest.NewServer to exercise the registered route. section 8.6
Listing 8.31 handler.go: Handler with a route
func HelloHandler(mux *http.ServeMux) { #1
mux.HandleFunc("/hello", func(w http.ResponseWriter, req *http.Request) {
switch req.Method { #2
case http.MethodGet:
HelloGet(w, req)
case http.MethodPost:
HelloPost(w, req)
default: #3
w.WriteHeader(http.StatusNotFound)
w.Write([]byte("invalid hello method"))
}
})
}
The /hello registration shows where routing ends and method-specific handler logic begins. section 8.6
In a larger service, keep the HTTP layer focused on translating requests into domain operations and presenting results. A read-only /articles handler can depend on an ArticleReader interface rather than a storage interface that also permits writes. Its GET route accepts a count query parameter; the executable initializes storage, registers the routes, and starts the server. section 11.3.2
Listing 11.4 HTTP handlers with an injected ArticleReader
package handlers
import (
"encoding/json"
"net/http"
"strconv"
"github.com/YOUR_USERNAME/go-news/domain"
)
type Handlers struct { #1
articles domain.ArticleReader
}
func New(articles domain.ArticleReader) *Handlers { #2
return &Handlers{
articles: articles,
}
}
func (h *Handlers) RegisterRoutes(mux *http.ServeMux) { #3
mux.HandleFunc("/articles", h.articlesHandler)
}
func (h *Handlers) articlesHandler(w http.ResponseWriter, r *http.Request) { #4
if r.Method != http.MethodGet {
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}
n := 10
if countStr := r.URL.Query().Get("count"); countStr != "" { #5
if count, err := strconv.Atoi(countStr); err == nil && count > 0 {
n = count
}
}
articles := h.articles.GetRecent(n)
w.Header().Set("Content-Type", "application/json") #6
if err := json.NewEncoder(w).Encode(articles); err != nil {
http.Error(w, "failed to encode response", http.StatusInternalServerError)
return
}
}
The article handler illustrates a route backed by a narrow, injected domain interface. section 11.3.2

























