SlideShare a Scribd company logo
net/http and the
http.Handler interface
(or simply put, building web servers in Go)
Who am I?
● Work at
● Go developer since 2013
● Currently building cloud based energy
optimization system for commercial buildings
(using Go)
● Joakim Gustin
Agenda
● The basics
● Routing
● Middleware
● Accessing “globals”
● Testing
● Tips and tricks
HTTP in Go
● Built in web server (net/http)
● Core piece of the Go standard library
● Can be run facing clients (even Cloudflare does)
● Handles TLS
● Automatic support for HTTP/2
net/http - ListenAndServe(this blue means
official GoDoc)
The http.Handler interface
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
type ResponseWriter interface {
Header() Header
WriteHeader(statusCode int)
Write([]byte) (int, error)
}
type Request struct {
Method string
URL *url.URL
Body io.ReadCloser
// … rest omitted
}
The naive approach
import “net/http”
type myServer struct {}
func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello, Go West”)
}
}
func main() {
http.ListenAndServe(":8080", myServer)
}
How about some routing?
How about some routing?
import “net/http”
type myServer struct {}
func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello, Go West”)
}
}
func main() {
http.Handle("/hello", myHandler)
http.ListenAndServe(":8080", nil) // nil, wut?
}
The http.ServeMux
import “net/http”
type myHandler struct {}
func (mh *myHandler) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello”)
}
}
func main() {
mux := http.NewServeMux()
mux.Handle("/hello", myHandler)
http.ListenAndServe(":8080", mux) // phew, much better
}
The http.ServeMux
Can we make this part simpler?
type myServer struct {}
// implementing the http.Handler interface
func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello”)
}
}
Revisiting the go type system
type myServer struct {} // Define a type
func (s myServer) DoSomething() {} // Define a method on it
type serverList []myServer // A type can be many things
func (sl serverList) DoSomething() {} // yep, valid
// wait a minute, functions are first class citizens in Go, right?
type mathFunc func(int) int
func (mf mathFunc) DoSomething() {} // OMG, yes it’s valid
A function implementing an interface?!
type HandlerFunc func(http.ResponseWriter, *http.Request)
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
type Handler interface {
ServeHTTP(ResponseWriter, *Request)
}
SURPRISE!
Revisiting our example
import “net/http”
// Look ma, no struct!
func myHandler(rw ResponseWriter, req *Request) {
if req.method == “GET” {
rw.Write([]byte(“hello”)
}
}
func main() {
mux := http.NewServeMux()
mux.Handle("/hello", http.HandlerFunc(myHandler))
log.Fatal(http.ListenAndServe(":8080", mux))
}
Revisiting our example
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/hello", myHandler)
log.Fatal(http.ListenAndServe(":8080", mux))}
}
Lets talk about
routing
Routing the std lib way
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/edit/", editHandler)
http.ListenAndServe(":8080", mux)
}
$ curl localhost:8080/edit/my-page
$ # yeah, alright, but what about
$ curl localhost:8080/edit/my-page/revision/2
Third party to the rescue!
func editHandler(w http.ResponseWriter, r *http.Request) {
title := chi.URLParam(r, “title”)
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/{title}", editHandler)
mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler)
http.ListenAndServe(":8080", mux)
}
$ go get github.com/go-chi/chi
Third party to the rescue!
func editHandler(w http.ResponseWriter, r *http.Request) {
title := r.URL.Path[len("/edit/"):]
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/", editHandler)
mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler)
http.ListenAndServe(":8080", mux)
}
$ go get github.com/go-chi/chi
What about
middleware?
Net/http and the http.handler interface
type middleware func(next http.Handler) http.Handler
The middleware signature
Simple logging middleware
func logger(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
log.Println("Before")
h.ServeHTTP(w, r) // call wrapped handler
log.Println("After")
})
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/", logger(editHandler))
http.ListenAndServe(":8080", mux)
}
Control flow
func authenticated(h http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if (isAuthenticated) { // Perform application auth
h.ServeHTTP(w, r) // call wrapped handler
} else {
w.WriteHeader(http.StatusUnauthorized)
}
})
}
func main() {
mux := chi.NewRouter()
mux.Get("/edit/", authenticated(editHandler))
http.ListenAndServe(":8080", mux)
}
In go-chi
func main() {
mux := chi.NewRouter()
// Base middleware
mux.use(logger)
mux.use(rateLimiter)
// Instead of logger(ratelimiter(editHandler))
mux.Get("/edit/", editHandler)
r.Route("/admin", func(r chi.Router) {
r.use(authenticated)
// Instead of logger(ratelimiter(authenticated(adminHandler))
r.Get(“/”, adminHandler)
}
http.ListenAndServe(":8080", mux)
}
Accessing
“globals”
Please no package level variables
var (
db *sql.DB
cache redis.Client
)
func updateHandler(w http.ResponseWriter, r *http.Request) {
lastSeen := time.Now()
db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1)
cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen)
}
func main() {
mux := chi.NewRouter()
mux.Get(“/users/update”, updateHandler)
http.ListenAndServe(":8080", mux)
}
Inject the dependencies instead!
type HTTPEnv struct {
db *sql.DB
cache redis.Client
logger log.Logger
}
func makeUpdateHandler(env *HTTPEnv) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
lastSeen := time.Now()
env.db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1)
env.cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen)
})
}
func main() {
mux := chi.NewRouter()
env := HTTPEnv{} // Setup a global env here
mux.Get(“/users/update”, makeUpdateHandler(env))
http.ListenAndServe(":8080", mux)
}
Testing http
Creating an HTTP request
req, err := http.NewRequest(“GET”, “localhost:3000/my-route”, nil)
Testing an individual handler
import “net/http/httptest”
func TestMyHandler(t *testing.T) {
req := httptest.NewRequest("GET", "/2018-03-07", nil)
w := httptest.NewRecorder()
updateHandler(w, req)
// Assert stuff on w
}
Testing an entire server
import “net/http/httptest”
func TestMyHandler(t *testing.T) {
mux := myapp.NewRootMux() // Let’s pretend this exists
// Now you will get the entire chain of middleware etc
ts := httptest.NewServer(mux)
defer ts.Close()
res, _:= http.Get(ts.URL + “/edit/2018-03-07”)
// Assert stuff on res
}
Tips and tricks
Anonymous struct
type userRequest struct {
username string
password string
}
func createUserHandler(rw http.ResponseWriter, r *http.Request) {
var user userRequest
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
w.write([]byte(“an error occured”))
}
data := struct {
Message string
Username string
}{
Message: “Success!”,
Username: user.username,
}
json.NewEncoder(rw).Encode(data)
}
A small gotcha...
func createUserHandler(rw http.ResponseWriter, r *http.Request) {
var user userRequest
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
w.write([]byte(“an error occured”))
// forgot to return, panic!
}
data := struct {
Message string
Username string
}{
Message: “Success!”,
Username: user.username,
}
json.NewEncoder(rw).Encode(data)
}
An altered (better?) interface
type HandlerFunc func(http.ResponseWriter, *http.Request) error
Clearer now?
func createUserHandler(w http.ResponseWriter, r *http.Request) error {
var user userRequest
if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
return errors.New(“an error occured!”)
}
data := struct {
Message string
Token string
}{
title,
users,
}
json.NewEncoder(w).Encode(data)
return nil
}
Pluggin’ it in
type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error
func ErrorHandler(h ErrorHandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := f(w, r); err != nil {
// bonus, centralized http error handling
w.Write([]byte(“an error occured”)
}
})
}
func main() {
mux := chi.NewRouter()
mux.Get(“/users/update”, ErrorHandler(updateHandler))
http.ListenAndServe(":8080", mux)
}
What about HTTP status codes?
type HTTPError struct {
err error // The actual error
statusCode int
}
func (h HTTPError) Error() string {
return h.err.Error() // Just pass along the original errors message
}
type error interface {
Error() string
}
Handling HTTP Error
type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error
func ErrorHandler(h ErrorHandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if err := f(w, r); err != nil { // an error occurred
switch e := err.(type) {
case *HTTPError:
w.WriteHeader(e.statusCode)
default:
w.WriteHeader(http.StatusInternalServerError) // aka 500
}
}
})
}

More Related Content

PDF
What's new in jQuery 1.5
PPTX
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
PDF
Reactive, component 그리고 angular2
PDF
Understanding Asynchronous JavaScript
PDF
Functional Reactive Programming - RxSwift
PPTX
Javascript Execution Context Flow
PDF
JavaScript patterns
PPTX
Javascript basics for automation testing
What's new in jQuery 1.5
Callbacks, Promises, and Coroutines (oh my!): Asynchronous Programming Patter...
Reactive, component 그리고 angular2
Understanding Asynchronous JavaScript
Functional Reactive Programming - RxSwift
Javascript Execution Context Flow
JavaScript patterns
Javascript basics for automation testing

What's hot (14)

KEY
jrubykaigi2010-lt-rubeus
PPTX
Intro to Javascript
PDF
運用Closure Compiler 打造高品質的JavaScript
PDF
Hack tutorial
PPTX
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
PPTX
Mongoose and MongoDB 101
PPTX
10. session 10 loops and arrays
PPTX
Javascript forloop-let
PDF
Introduction to Node.js
PDF
Reactive Web - Servlet & Async, Non-blocking I/O
PDF
jQuery - Chapter 5 - Ajax
PDF
Selectors and normalizing state shape
PDF
Introduction to Nodejs
PDF
Introducere in web
jrubykaigi2010-lt-rubeus
Intro to Javascript
運用Closure Compiler 打造高品質的JavaScript
Hack tutorial
Venturing Into The Wild: A .NET Developer's Experience As A Ruby Developer
Mongoose and MongoDB 101
10. session 10 loops and arrays
Javascript forloop-let
Introduction to Node.js
Reactive Web - Servlet & Async, Non-blocking I/O
jQuery - Chapter 5 - Ajax
Selectors and normalizing state shape
Introduction to Nodejs
Introducere in web
Ad

Similar to Net/http and the http.handler interface (20)

PDF
May 2010 - RestEasy
PDF
PDF
RESTful Web Applications with Google Go
PDF
Server Side Swift with Swag
PDF
async/await in Swift
PDF
Protocol-Oriented Networking
PDF
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
PPTX
Intro to Node
PDF
服务框架: Thrift & PasteScript
PPTX
Golang slidesaudrey
PDF
Android Best Practices
PDF
Server Side Swift: Vapor
PDF
外部環境への依存をテストする
PDF
Clean coding-practices
PDF
神に近づくx/net/context (Finding God with x/net/context)
PPTX
Ajax
PDF
Cервер на Go для мобильной стратегии
PDF
Go for the Paranoid Network Programmer, 2025 Edition
KEY
Building @Anywhere (for TXJS)
May 2010 - RestEasy
RESTful Web Applications with Google Go
Server Side Swift with Swag
async/await in Swift
Protocol-Oriented Networking
GDG Devfest 2019 - Build go kit microservices at kubernetes with ease
Intro to Node
服务框架: Thrift & PasteScript
Golang slidesaudrey
Android Best Practices
Server Side Swift: Vapor
外部環境への依存をテストする
Clean coding-practices
神に近づくx/net/context (Finding God with x/net/context)
Ajax
Cервер на Go для мобильной стратегии
Go for the Paranoid Network Programmer, 2025 Edition
Building @Anywhere (for TXJS)
Ad

Recently uploaded (20)

PPTX
Machine Learning_overview_presentation.pptx
PPTX
Programs and apps: productivity, graphics, security and other tools
PPTX
A Presentation on Artificial Intelligence
PDF
Empathic Computing: Creating Shared Understanding
PDF
NewMind AI Weekly Chronicles - August'25-Week II
PDF
Mobile App Security Testing_ A Comprehensive Guide.pdf
PDF
Reach Out and Touch Someone: Haptics and Empathic Computing
PDF
Encapsulation theory and applications.pdf
PDF
Univ-Connecticut-ChatGPT-Presentaion.pdf
PDF
Per capita expenditure prediction using model stacking based on satellite ima...
PPTX
Digital-Transformation-Roadmap-for-Companies.pptx
PDF
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
PDF
Advanced methodologies resolving dimensionality complications for autism neur...
PPTX
OMC Textile Division Presentation 2021.pptx
PDF
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
PDF
gpt5_lecture_notes_comprehensive_20250812015547.pdf
PDF
Assigned Numbers - 2025 - Bluetooth® Document
PPTX
Tartificialntelligence_presentation.pptx
PDF
Machine learning based COVID-19 study performance prediction
PDF
Approach and Philosophy of On baking technology
Machine Learning_overview_presentation.pptx
Programs and apps: productivity, graphics, security and other tools
A Presentation on Artificial Intelligence
Empathic Computing: Creating Shared Understanding
NewMind AI Weekly Chronicles - August'25-Week II
Mobile App Security Testing_ A Comprehensive Guide.pdf
Reach Out and Touch Someone: Haptics and Empathic Computing
Encapsulation theory and applications.pdf
Univ-Connecticut-ChatGPT-Presentaion.pdf
Per capita expenditure prediction using model stacking based on satellite ima...
Digital-Transformation-Roadmap-for-Companies.pptx
Profit Center Accounting in SAP S/4HANA, S4F28 Col11
Advanced methodologies resolving dimensionality complications for autism neur...
OMC Textile Division Presentation 2021.pptx
Architecting across the Boundaries of two Complex Domains - Healthcare & Tech...
gpt5_lecture_notes_comprehensive_20250812015547.pdf
Assigned Numbers - 2025 - Bluetooth® Document
Tartificialntelligence_presentation.pptx
Machine learning based COVID-19 study performance prediction
Approach and Philosophy of On baking technology

Net/http and the http.handler interface

  • 1. net/http and the http.Handler interface (or simply put, building web servers in Go)
  • 2. Who am I? ● Work at ● Go developer since 2013 ● Currently building cloud based energy optimization system for commercial buildings (using Go) ● Joakim Gustin
  • 3. Agenda ● The basics ● Routing ● Middleware ● Accessing “globals” ● Testing ● Tips and tricks
  • 4. HTTP in Go ● Built in web server (net/http) ● Core piece of the Go standard library ● Can be run facing clients (even Cloudflare does) ● Handles TLS ● Automatic support for HTTP/2
  • 5. net/http - ListenAndServe(this blue means official GoDoc)
  • 6. The http.Handler interface type Handler interface { ServeHTTP(ResponseWriter, *Request) } type ResponseWriter interface { Header() Header WriteHeader(statusCode int) Write([]byte) (int, error) } type Request struct { Method string URL *url.URL Body io.ReadCloser // … rest omitted }
  • 7. The naive approach import “net/http” type myServer struct {} func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello, Go West”) } } func main() { http.ListenAndServe(":8080", myServer) }
  • 8. How about some routing?
  • 9. How about some routing? import “net/http” type myServer struct {} func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello, Go West”) } } func main() { http.Handle("/hello", myHandler) http.ListenAndServe(":8080", nil) // nil, wut? }
  • 10. The http.ServeMux import “net/http” type myHandler struct {} func (mh *myHandler) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello”) } } func main() { mux := http.NewServeMux() mux.Handle("/hello", myHandler) http.ListenAndServe(":8080", mux) // phew, much better }
  • 12. Can we make this part simpler? type myServer struct {} // implementing the http.Handler interface func (ms *myServer) ServeHTTP(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello”) } }
  • 13. Revisiting the go type system type myServer struct {} // Define a type func (s myServer) DoSomething() {} // Define a method on it type serverList []myServer // A type can be many things func (sl serverList) DoSomething() {} // yep, valid // wait a minute, functions are first class citizens in Go, right? type mathFunc func(int) int func (mf mathFunc) DoSomething() {} // OMG, yes it’s valid
  • 14. A function implementing an interface?! type HandlerFunc func(http.ResponseWriter, *http.Request) func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) { f(w, r) } type Handler interface { ServeHTTP(ResponseWriter, *Request) }
  • 16. Revisiting our example import “net/http” // Look ma, no struct! func myHandler(rw ResponseWriter, req *Request) { if req.method == “GET” { rw.Write([]byte(“hello”) } } func main() { mux := http.NewServeMux() mux.Handle("/hello", http.HandlerFunc(myHandler)) log.Fatal(http.ListenAndServe(":8080", mux)) }
  • 17. Revisiting our example func main() { mux := http.NewServeMux() mux.HandleFunc("/hello", myHandler) log.Fatal(http.ListenAndServe(":8080", mux))} }
  • 19. Routing the std lib way func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] } func main() { mux := http.NewServeMux() mux.HandleFunc("/edit/", editHandler) http.ListenAndServe(":8080", mux) } $ curl localhost:8080/edit/my-page $ # yeah, alright, but what about $ curl localhost:8080/edit/my-page/revision/2
  • 20. Third party to the rescue! func editHandler(w http.ResponseWriter, r *http.Request) { title := chi.URLParam(r, “title”) } func main() { mux := chi.NewRouter() mux.Get("/edit/{title}", editHandler) mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler) http.ListenAndServe(":8080", mux) } $ go get github.com/go-chi/chi
  • 21. Third party to the rescue! func editHandler(w http.ResponseWriter, r *http.Request) { title := r.URL.Path[len("/edit/"):] } func main() { mux := chi.NewRouter() mux.Get("/edit/", editHandler) mux.Patch("/{month}-{day}-{year}/update", fancyPatchHandler) http.ListenAndServe(":8080", mux) } $ go get github.com/go-chi/chi
  • 24. type middleware func(next http.Handler) http.Handler The middleware signature
  • 25. Simple logging middleware func logger(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { log.Println("Before") h.ServeHTTP(w, r) // call wrapped handler log.Println("After") }) } func main() { mux := chi.NewRouter() mux.Get("/edit/", logger(editHandler)) http.ListenAndServe(":8080", mux) }
  • 26. Control flow func authenticated(h http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if (isAuthenticated) { // Perform application auth h.ServeHTTP(w, r) // call wrapped handler } else { w.WriteHeader(http.StatusUnauthorized) } }) } func main() { mux := chi.NewRouter() mux.Get("/edit/", authenticated(editHandler)) http.ListenAndServe(":8080", mux) }
  • 27. In go-chi func main() { mux := chi.NewRouter() // Base middleware mux.use(logger) mux.use(rateLimiter) // Instead of logger(ratelimiter(editHandler)) mux.Get("/edit/", editHandler) r.Route("/admin", func(r chi.Router) { r.use(authenticated) // Instead of logger(ratelimiter(authenticated(adminHandler)) r.Get(“/”, adminHandler) } http.ListenAndServe(":8080", mux) }
  • 29. Please no package level variables var ( db *sql.DB cache redis.Client ) func updateHandler(w http.ResponseWriter, r *http.Request) { lastSeen := time.Now() db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1) cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen) } func main() { mux := chi.NewRouter() mux.Get(“/users/update”, updateHandler) http.ListenAndServe(":8080", mux) }
  • 30. Inject the dependencies instead! type HTTPEnv struct { db *sql.DB cache redis.Client logger log.Logger } func makeUpdateHandler(env *HTTPEnv) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { lastSeen := time.Now() env.db.Query(`UPDATE users SET last_seen = ? WHERE id = ?`, lastSeen, 1) env.cache.Set(fmt.Sprintf(“%d_last_seen”, 1), lastSeen) }) } func main() { mux := chi.NewRouter() env := HTTPEnv{} // Setup a global env here mux.Get(“/users/update”, makeUpdateHandler(env)) http.ListenAndServe(":8080", mux) }
  • 32. Creating an HTTP request req, err := http.NewRequest(“GET”, “localhost:3000/my-route”, nil)
  • 33. Testing an individual handler import “net/http/httptest” func TestMyHandler(t *testing.T) { req := httptest.NewRequest("GET", "/2018-03-07", nil) w := httptest.NewRecorder() updateHandler(w, req) // Assert stuff on w }
  • 34. Testing an entire server import “net/http/httptest” func TestMyHandler(t *testing.T) { mux := myapp.NewRootMux() // Let’s pretend this exists // Now you will get the entire chain of middleware etc ts := httptest.NewServer(mux) defer ts.Close() res, _:= http.Get(ts.URL + “/edit/2018-03-07”) // Assert stuff on res }
  • 36. Anonymous struct type userRequest struct { username string password string } func createUserHandler(rw http.ResponseWriter, r *http.Request) { var user userRequest if err := json.NewDecoder(r.Body).Decode(&user); err != nil { w.write([]byte(“an error occured”)) } data := struct { Message string Username string }{ Message: “Success!”, Username: user.username, } json.NewEncoder(rw).Encode(data) }
  • 37. A small gotcha... func createUserHandler(rw http.ResponseWriter, r *http.Request) { var user userRequest if err := json.NewDecoder(r.Body).Decode(&user); err != nil { w.write([]byte(“an error occured”)) // forgot to return, panic! } data := struct { Message string Username string }{ Message: “Success!”, Username: user.username, } json.NewEncoder(rw).Encode(data) }
  • 38. An altered (better?) interface type HandlerFunc func(http.ResponseWriter, *http.Request) error
  • 39. Clearer now? func createUserHandler(w http.ResponseWriter, r *http.Request) error { var user userRequest if err := json.NewDecoder(r.Body).Decode(&user); err != nil { return errors.New(“an error occured!”) } data := struct { Message string Token string }{ title, users, } json.NewEncoder(w).Encode(data) return nil }
  • 40. Pluggin’ it in type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error func ErrorHandler(h ErrorHandlerFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := f(w, r); err != nil { // bonus, centralized http error handling w.Write([]byte(“an error occured”) } }) } func main() { mux := chi.NewRouter() mux.Get(“/users/update”, ErrorHandler(updateHandler)) http.ListenAndServe(":8080", mux) }
  • 41. What about HTTP status codes? type HTTPError struct { err error // The actual error statusCode int } func (h HTTPError) Error() string { return h.err.Error() // Just pass along the original errors message } type error interface { Error() string }
  • 42. Handling HTTP Error type ErrorHandlerFunc func(http.ResponseWriter, *http.Request) error func ErrorHandler(h ErrorHandlerFunc) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { if err := f(w, r); err != nil { // an error occurred switch e := err.(type) { case *HTTPError: w.WriteHeader(e.statusCode) default: w.WriteHeader(http.StatusInternalServerError) // aka 500 } } }) }