Initial checkin

This commit is contained in:
Justin Hawkins 2025-03-10 14:46:06 +10:30
commit 5faf40c2ab
6 changed files with 165 additions and 0 deletions

14
README.md Normal file
View File

@ -0,0 +1,14 @@
## path values
The three examples to go with the blog post at https://hawkins.id.au/posts/go-mux/path-values
These should all work identically, each just shows a different way of achieving
the same thing - extracing a user id from the path and making the user struct
(instantiated from some sort of database, faked in common/db/db.go for this
example) available to the route handler.
go run go-mux/pathvalues/1-basic
go run go-mux/pathvalues/2-slightly-less-basic
go run go-mux/pathvalues/3-middleware

22
common/db/db.go Normal file
View File

@ -0,0 +1,22 @@
package db
import "fmt"
type User struct {
ID int
}
func LoadUser(id int) (User, error) {
if id < 0 {
return User{}, fmt.Errorf("invalid negative user id '%d'", id)
}
if id > 100 {
return User{}, fmt.Errorf("invalid user id '%d'", id)
}
return User{ID: id}, nil
}
func (u User) String() string {
return fmt.Sprintf("this user is id: %d", u.ID)
}

3
go.mod Normal file
View File

@ -0,0 +1,3 @@
module go-mux
go 1.24.1

View File

@ -0,0 +1,36 @@
package main
import (
"fmt"
"go-mux/common/db"
"log/slog"
"net/http"
"strconv"
)
func main() {
http.HandleFunc("/user/{userId}/view", viewUser)
slog.Info("starting web service on :8080")
panic(http.ListenAndServe(":8080", nil))
}
func viewUser(w http.ResponseWriter, r *http.Request) {
userID := r.PathValue("userId")
userIDint, err := strconv.Atoi(userID)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("bad user id"))
return
}
user, err := db.LoadUser(userIDint)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("could not load user: %s", err.Error())))
return
}
slog.Info("loaded user")
// show the user id
w.Write([]byte(fmt.Sprintf("loaded user %s", user)))
}

View File

@ -0,0 +1,42 @@
package main
import (
"fmt"
"go-mux/common/db"
"log/slog"
"net/http"
"strconv"
)
func main() {
http.HandleFunc("/user/{userId}/view", viewUser)
slog.Info("starting web service on :8080")
panic(http.ListenAndServe(":8080", nil))
}
func viewUser(w http.ResponseWriter, r *http.Request) {
user, err := loadUserFromRequest(r)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("could not load user: %s", err.Error())))
return
}
slog.Info("loaded user")
// show the user id
w.Write([]byte(fmt.Sprintf("loaded user %s", user)))
}
func loadUserFromRequest(r *http.Request) (db.User, error) {
userID := r.PathValue("userId")
userIDint, err := strconv.Atoi(userID)
if err != nil {
return db.User{}, err
}
user, err := db.LoadUser(userIDint)
if err != nil {
return db.User{}, err
}
return user, nil
}

View File

@ -0,0 +1,48 @@
package main
import (
"context"
"fmt"
"go-mux/common/db"
"log/slog"
"net/http"
"strconv"
)
var userContextKey = "user"
func main() {
http.Handle("/user/{userId}/view", getUserFromPath(http.HandlerFunc(viewUser)))
slog.Info("starting web service on :8080")
panic(http.ListenAndServe(":8080", nil))
}
func viewUser(w http.ResponseWriter, r *http.Request) {
user := r.Context().Value(userContextKey).(db.User)
slog.Info("loaded user")
// show the user id
w.Write([]byte(fmt.Sprintf("loaded user %s", user.String())))
}
func getUserFromPath(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
userID := r.PathValue("userId")
userIDint, err := strconv.Atoi(userID)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte("bad user id"))
return
}
user, err := db.LoadUser(userIDint)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
w.Write([]byte(fmt.Sprintf("could not load user: %s", err.Error())))
return
}
newContext := context.WithValue(r.Context(), userContextKey, user)
r = r.Clone(newContext)
next.ServeHTTP(w, r)
})
}