Skip to content

Instantly share code, notes, and snippets.

@gsrai
Created May 20, 2024 20:36
Show Gist options
  • Save gsrai/a3a1e20173814c204edbb2d764b61eef to your computer and use it in GitHub Desktop.
Save gsrai/a3a1e20173814c204edbb2d764b61eef to your computer and use it in GitHub Desktop.
Web Dev in Go 1.22

Web Dev in Go 1.22

Go 1.22 now ships with an HTTP server multiplexer, so there is no longer a need to include routing libraries like httprouter or chi.

Basic use of http.ServeMux

package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"
)

func timeHandler(w http.ResponseWriter, r *http.Request) {
	now := time.Now().Format(time.RFC1123)
	fmt.Fprintf(w, "The time is: %v", now)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /time", timeHandler)

	err := http.ListenAndServe(":3000", mux)
	if err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatalf("Server startup failed: %s", err)
	}
}

Path parameters

package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
)

func itemHandler(w http.ResponseWriter, r *http.Request) {
	id := r.PathValue("id")
	fmt.Fprintf(w, "received request for item: %v", id)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /item/{id}", itemHandler)

	err := http.ListenAndServe(":3000", mux)
	if err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatalf("Server startup failed: %s", err)
	}
}

Middleware

package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"
)

type Middleware func(handler http.Handler) http.Handler

func CombineMiddleware(middlewares ...Middleware) Middleware {
	return func(next http.Handler) http.Handler {
		for _, mw := range middlewares {
			next = mw(next)
		}
		return next
	}
}

func RequestLogger(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		defer func() {
			log.Println(r.Method, r.URL.Path, time.Since(start))
		}()
		next.ServeHTTP(w, r)
	})
}

func timeHandler(w http.ResponseWriter, r *http.Request) {
	now := time.Now().Format(time.RFC1123)
	fmt.Fprintf(w, "The time is: %v", now)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /time", timeHandler)

	middlewares := CombineMiddleware(
		RequestLogger,
	)

	err := http.ListenAndServe(":3000", middlewares(mux))
	if err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatalf("Server startup failed: %s", err)
	}
}

Subrouting

package main

import (
	"errors"
	"fmt"
	"log"
	"net/http"
	"time"
)

func timeHandler(w http.ResponseWriter, r *http.Request) {
	now := time.Now().Format(time.RFC1123)
	fmt.Fprintf(w, "The time is: %v", now)
}

func main() {
	mux := http.NewServeMux()
	mux.HandleFunc("GET /time", timeHandler)
	
	v1 := http.NewServeMux()
	v1.Handle("/v1/", http.StripPrefix("/v1", mux))

	err := http.ListenAndServe(":3000", mux)
	if err != nil && !errors.Is(err, http.ErrServerClosed) {
		log.Fatalf("Server startup failed: %s", err)
	}
}

Testing

Also, you can use httptest to test HTTP request handlers.

Other resources

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment