Golang(11)Web Service - RESTful
1. Concept
Requests can be matched based on URL host, path, path prefix, schemes, header and query values, HTTP methods or using custom matchers.
URL hosts and paths can have variables with an optional regular expression.
It implements the http.Handler interface so it is compatible with the standard http.ServeMux.
router := mux.NewRouter()
router.HandleFunc("/", HomeHandler)
router.HandleFunc("/products", ProductsHandler)
Once the router matches, the handler will be called passing (http.ResponseWriter, http.Requet)as parameters.
Variables in paths follow this pattern, {name} or {name:pattern}.
router.HandleFunc(“/products/{key}”, ProductHandler)
router.HandleFunc(“/articles/{category}/{id:[0-9]+}”, ArticleHandler)
We can fetch all these values from map
vars :=mux.Vars(request)
category := vars[“category"]
Some other matches
router.Host(“www.domain.com”)
router.Host(“{subdomain:[a-z]+}.domain.com”)
router.PathPrefix(“/products/“)
router.Methods(“GET”, “POST”)
router.Schemes(“https”)
router.Headers(“X-Requested-With”, “XMLHttpRequest”)
router.Queries(“key”, “value”)
Example:
router.HandleFunc(“/products”, ProductsHandler).Host(“www.domain.com”).Methods(“GET”).Schemes(“http”)
Work with Sub Router
router := mux.NewRouter()
sub := router.Host(“www.domain.com”).Subrouter()
sub.HandleFunc(“/products/“, ProductsHandler)
Name a Router and Build the URL
router := mux.NewRouter()
router.HandleFunc(“/articles/{category}/{id:[0-9]+}”, ArticleHandler).Name(“article”)
url, err := r.Get(“article”).URL(“category”, “technology”, “id”, “42”)
eg: “/articles/technology/42"
2. Installation
>go get github.com/gorilla/mux
Here is the Whole go implementation Class as follow:
package main
import (
"encoding/json” //import the json package to marshal and unmarshal JSON
"fmt"
"github.com/gorilla/mux” //here is the router and dispatcher
"io/ioutil” //io util to read/write to io
"net/http” //handle the http based request and response
)
type Bug struct { //my demo object in go
Id string
BugNumber string
BugName string
BugDesn string
}
type BugResult struct { //my common REST response object
Bugs []Bug
Status string
}
func getBug(w http.ResponseWriter, r *http.Request) { //get method
b1 := Bug{Id: "1", BugNumber: "bug1", BugName: "bug1", BugDesn: "desn1"}
re := BugResult{Status: "Ok", Bugs: []Bug{b1}}
b, err := json.Marshal(re) //marshal the object to JSON
if err != nil {
fmt.Fprint(w, "json err: %s", err)
}
fmt.Fprint(w, string(b)) //write to the response
}
func updateBug(w http.ResponseWriter, r *http.Request) {
var b Bug
c, err := ioutil.ReadAll(r.Body) //read the string from body and unmarshal
fmt.Println("updateBug called with Body=" + string(c))
json.Unmarshal(c, &b)
fmt.Println("updateBug called with Id=" + b.Id)
fmt.Println("updateBug called with BugNumber=" + b.BugNumber)
fmt.Println("updateBug called with BugName=" + b.BugName)
fmt.Println("updateBug called with BugDesn=" + b.BugDesn)
re := BugResult{Status: "OK", Bugs: []Bug{b}}
re_json, err := json.Marshal(re)
if err != nil {
fmt.Fprint(w, "json err: %s", err)
}
fmt.Fprint(w, string(re_json))
}
func deleteBug(w http.ResponseWriter, r *http.Request) {
id := mux.Vars(r)["Id”] //get the param from Path
fmt.Println("deleteBug called with Id = ", id)
re := BugResult{Status: "OK"}
b, err := json.Marshal(re)
if err != nil {
fmt.Fprint(w, "json err: %s", err)
}
fmt.Fprint(w, string(b))
}
func addBug(w http.ResponseWriter, r *http.Request) {
var b Bug
content, err := ioutil.ReadAll(r.Body)
fmt.Println("addBug called with Body=" + string(content))
json.Unmarshal(content, &b)
fmt.Println("addBug called with BugNumber=" + b.BugNumber)
fmt.Println("addBug called with BugName=" + b.BugName)
fmt.Println("addBug called with BugDesn=" + b.BugDesn)
re := BugResult{Status: "OK", Bugs: []Bug{b}}
re_json, err := json.Marshal(re)
if err != nil {
fmt.Fprint(w, "json err: %s", err)
}
fmt.Fprint(w, string(re_json))
}
func listBug(w http.ResponseWriter, r *http.Request) {
b1 := Bug{Id: "1", BugNumber: "bug1", BugName: "bug1", BugDesn: "desn1"}
b2 := Bug{Id: "2", BugNumber: "bug2", BugName: "bug2", BugDesn: "desn2"}
re := BugResult{Status: "Ok", Bugs: []Bug{b1, b2}}
b, err := json.Marshal(re)
if err != nil {
fmt.Fprint(w, "json err: %s", err)
}
fmt.Fprint(w, string(b))
}
func main() {
router := mux.NewRouter()
router.HandleFunc("/bugs", listBug).Methods("GET")
router.HandleFunc("/bugs", addBug).Methods("POST")
router.HandleFunc("/bugs/{Id}", getBug).Methods("GET")
router.HandleFunc("/bugs/{Id}", updateBug).Methods("PUT")
router.HandleFunc("/bugs/{Id}", deleteBug).Methods("DELETE")
http.Handle("/", router)
http.ListenAndServe(":8088", nil)
}
It is not very nice since there is repeat codes like this.
b, err := json.Marshal(re)
if err != nil {
fmt.Fprint(w, "json err: %s", err)
}
fmt.Fprint(w, string(b))
I will check how to avoid that.
References:
https://github.com/drone/routes
https://github.com/astaxie/build-web-application-with-golang/blob/master/ebook/08.3.md
https://github.com/gorilla/mux
http://www.gorillatoolkit.org/pkg/mux
JSON handle
http://sillycat.iteye.com/blog/2065585