In rocket.rs, we've this simple route code:
#[get("/hello/<name>/<age>")]
fn hello(name: &str, age: u8) -> String {
format!("Hello, {} year old named {}!", age, name)
}
where if you were to visit http://localhost:8000/hello/John/58
in the browser, you’d see:
Hello, 58 year old named John!
I read this, but the accepted answer is about a way to do Go url parameters mapping
for single route, that could read http://localhost:8080/blob/123/test
as /blob/{id}/test
and display the required route.
I know there are some great routers/frameworks there, but looking to build simple code myself to understand http route handlers in a better way.
Let's say I've:
type Tender struct {
tenderReference string
venderCode int
}
func (t Tender) readWrite() {
fmt.Printf("Tender %s is ready for vendor %d to review and submit\n", t.tenderReference, t.venderCode)
}
func (t Tender) readOnly(w http.ResponseWriter, r *http.Request) {
fmt.Printf("Tender %s already submitted by vender %d\n", t.tenderReference, t.venderCode)
}
And want my routes to be something like:
/api/tender/readWrite/{tenderReference}/vendor/{venderCode}
that is callingfunc (t Tender) readWrite(){}
/api/tender/readOnly/{tenderReference}/vendor/{venderCode}
that is callingfunc (t Tender) readOnly(){}
How many route handler do I have to build?
CodePudding user response:
I solved it as below, other thoughts are welcomed:
404.go
package main
import (
"fmt"
"net/http"
)
func handle404(w http.ResponseWriter, r *http.Request) {
fmt.Fprint(w, "mmm, it looks you are playing around, page is not available :)\n")
}
getField.go
package main
import "net/http"
type ctxKey struct{}
func getField(r *http.Request, index int) string {
fields := r.Context().Value(ctxKey{}).([]string)
return fields[index]
}
routes.go
package main
import (
"net/http"
"regexp"
)
type route struct {
method string
regex *regexp.Regexp
handler http.HandlerFunc
}
var routes = []route{
newRoute("GET", "/api/tender/(rw|r)/([^/] )/vendor/([0-9] )", apiTenders),
}
func newRoute(method, pattern string, handler http.HandlerFunc) route {
return route{method, regexp.MustCompile("^" pattern "$"), handler}
}
tendor.go
package main
import (
"fmt"
"net/http"
"strconv"
)
type Tender struct {
tenderReference string
venderCode int
}
// Handles GET /api/tender/(rw|r)/([^/] )/vendor/([0-9] )
func apiTenders(w http.ResponseWriter, r *http.Request) {
action := getField(r, 0)
tenderReference := getField(r, 1)
venderCode, _ := strconv.Atoi(getField(r, 2))
tender := Tender{tenderReference, venderCode}
switch action {
case "rw":
tender.readWrite(w, r) // Display tender and allow vendor to submit feedback
case "r":
tender.readOnly(w, r) // Display readOnly copy of the tender
default:
fmt.Fprintf(w, "Tendert ERROR\n")
}
}
func (t Tender) readWrite(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Tender %s is ready for vendor %d to review and submit\n", t.tenderReference, t.venderCode)
}
func (t Tender) readOnly(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "Tender %s already submitted by vender %d\n", t.tenderReference, t.venderCode)
}
server.go
package main
import (
"context"
"fmt"
"net/http"
"strings"
)
type apiHandler struct{}
func main() {
http.Handle("/api/", apiHandler{})
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
// The "/" pattern matches everything, so we need to check
// that we're at the root here.
if req.URL.Path != "/" {
http.NotFound(w, req)
return
}
fmt.Fprintf(w, "Welcome to the home page!")
})
http.ListenAndServe(":8000", nil)
}
func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
var allow []string
for _, route := range routes {
matches := route.regex.FindStringSubmatch(r.URL.Path)
if len(matches) > 0 {
if r.Method != route.method {
allow = append(allow, route.method)
continue
}
ctx := context.WithValue(r.Context(), ctxKey{}, matches[1:])
route.handler(w, r.WithContext(ctx))
return
}
}
if len(allow) > 0 {
w.Header().Set("Allow", strings.Join(allow, ", "))
http.Error(w, "405 method not allowed", http.StatusMethodNotAllowed)
return
}
handle404(w, r)
//http.NotFound(w, r)
}