Back to Blog

Go net/http Server: Handlers, Routing & Middleware | Tutorial #32

Sandy LaneSandy Lane

Video: Go net/http Server: Handlers, Routing & Middleware | Tutorial #32 by Taught by Celeste AI - AI Coding Coach

Watch full page →

Building HTTP Servers in Go: Handlers, Routing & Middleware

Go's standard library net/http package provides powerful tools to create web servers without external frameworks. In this tutorial, you'll learn how to register handlers, route requests with ServeMux, implement middleware for logging and authentication, and build a JSON REST API with proper request parsing and response encoding.

Code

package main

import (
  "encoding/json"
  "log"
  "net/http"
  "time"
  "fmt"
)

// User represents a simple user model
type User struct {
  ID   string `json:"id"`
  Name string `json:"name"`
}

// In-memory user storage
var users = map[string]User{}

// Logging middleware logs request method, path, and duration
func loggingMiddleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    start := time.Now()
    next.ServeHTTP(w, r)
    duration := time.Since(start)
    log.Printf("%s %s took %v", r.Method, r.URL.Path, duration)
  })
}

// Auth middleware checks for X-API-Key header
func authMiddleware(next http.Handler) http.Handler {
  return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    apiKey := r.Header.Get("X-API-Key")
    if apiKey != "secret123" {
      http.Error(w, "Unauthorized", http.StatusUnauthorized)
      return
    }
    next.ServeHTTP(w, r)
  })
}

// Handler for GET /users - returns all users as JSON
func getUsersHandler(w http.ResponseWriter, r *http.Request) {
  w.Header().Set("Content-Type", "application/json")
  json.NewEncoder(w).Encode(users)
}

// Handler for POST /users - creates a new user from JSON body
func createUserHandler(w http.ResponseWriter, r *http.Request) {
  var user User
  if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
    http.Error(w, "Bad Request", http.StatusBadRequest)
    return
  }
  users[user.ID] = user
  w.WriteHeader(http.StatusCreated)
  json.NewEncoder(w).Encode(user)
}

func main() {
  mux := http.NewServeMux()

  // Register handlers with method patterns (Go 1.22+)
  mux.HandleFunc("GET /users", getUsersHandler)
  mux.HandleFunc("POST /users", createUserHandler)

  // Wrap mux with middleware
  handler := loggingMiddleware(authMiddleware(mux))

  fmt.Println("Server listening on :8080")
  http.ListenAndServe(":8080", handler)
}

Key Points

  • Use http.HandleFunc or ServeMux.HandleFunc with method patterns like "GET /users" to route requests.
  • Middleware functions wrap http.Handler to add cross-cutting concerns like logging and authentication.
  • Use http.ResponseWriter to write headers, status codes, and JSON responses with json.NewEncoder.
  • Parse JSON request bodies safely with json.NewDecoder and handle errors properly.
  • Start the server with http.ListenAndServe, passing the final handler chain including middleware.