Last active
October 6, 2018 21:37
-
-
Save lukearno/567288d4e102213553c2fac2946a91fc to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// inspired by https://blog.merovius.de/2017/06/18/how-not-to-use-an-http-router.html | |
package main | |
import ( | |
"fmt" | |
"net/http" | |
"path" | |
"strconv" | |
"strings" | |
) | |
func GETRoot(w http.ResponseWriter, r *http.Request, c *Context) { | |
fmt.Fprintf(w, "GET Root") | |
} | |
func GETTag(w http.ResponseWriter, r *http.Request, c *Context) { | |
fmt.Fprintf(w, "GET Tag") | |
} | |
func GETCustomers(w http.ResponseWriter, r *http.Request, c *Context) { | |
fmt.Fprintf(w, "GET Customers") | |
} | |
func GETCustomer(w http.ResponseWriter, r *http.Request, c *Context) { | |
fmt.Fprintf(w, "GET Customer %d", c.CustId) | |
} | |
func GETOrders(w http.ResponseWriter, r *http.Request, c *Context) { | |
fmt.Fprintf(w, "GET Orders for Customer %d", c.CustId) | |
} | |
func GETOrder(w http.ResponseWriter, r *http.Request, c *Context) { | |
fmt.Fprintf(w, "GET Order %d for Customer %d", c.OrderId, c.CustId) | |
} | |
type Path struct { | |
Parts []string | |
Index int | |
} | |
func (p *Path) Next() (next string) { | |
if !p.Done() { | |
next = p.Parts[p.Index] | |
p.Index++ | |
} | |
return | |
} | |
func (p *Path) This() (this string) { | |
if p.Index > 0 { | |
this = p.Parts[p.Index-1] | |
} | |
return | |
} | |
func (p *Path) Done() bool { | |
return p.Index == len(p.Parts) | |
} | |
type Handler func(w http.ResponseWriter, r *http.Request, c *Context) | |
type Endpoint struct { | |
GET Handler | |
POST Handler | |
PUT Handler | |
DELETE Handler | |
} | |
func Dispatch(w http.ResponseWriter, r *http.Request, c *Context, e *Endpoint) { | |
var h Handler | |
switch r.Method { | |
case "GET": | |
h = e.GET | |
case "POST": | |
h = e.POST | |
case "PUT": | |
h = e.PUT | |
case "DELETE": | |
h = e.DELETE | |
} | |
if h != nil { | |
h(w, r, c) // middleware here | |
} else { | |
http.Error(w, "Not Allowed", http.StatusMethodNotAllowed) | |
} | |
} | |
type Context struct { | |
CustId int | |
OrderId int | |
} | |
func Route(w http.ResponseWriter, r *http.Request) { | |
pth := Path{strings.Split(strings.Trim(path.Clean(r.URL.Path), "/"), "/"), 0} | |
cxt := Context{} | |
switch pth.Next() { | |
case "": | |
Dispatch(w, r, &cxt, &Endpoint{GET: GETRoot}) | |
case "tag": | |
Dispatch(w, r, &cxt, &Endpoint{GET: GETTag}) | |
case "customers": | |
switch pth.Next() { | |
case "": | |
Dispatch(w, r, &cxt, &Endpoint{GET: GETCustomers}) | |
default: | |
i, err := strconv.Atoi(pth.This()) | |
if err != nil { | |
goto notfound | |
} | |
cxt.CustId = i | |
switch pth.Next() { | |
case "": | |
Dispatch(w, r, &cxt, &Endpoint{GET: GETCustomer}) | |
case "orders": | |
switch pth.Next() { | |
case "": | |
Dispatch(w, r, &cxt, &Endpoint{GET: GETOrders}) | |
default: | |
i, err = strconv.Atoi(pth.This()) | |
if err != nil { | |
goto notfound | |
} | |
cxt.OrderId = i | |
Dispatch(w, r, &cxt, &Endpoint{GET: GETOrder}) | |
} | |
} | |
} | |
} | |
return | |
notfound: | |
http.Error(w, "Not Found", http.StatusNotFound) | |
} | |
type API struct{} | |
func (a *API) ServeHTTP(w http.ResponseWriter, r *http.Request) { | |
Route(w, r) | |
} | |
func main() { | |
http.ListenAndServe(":8000", &API{}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment