Let’s have a look at the handler function for HTTP Get to get Note resource values: //HTTP Get - /api/notes
func GetNoteHandler(w http.ResponseWriter, r *http.Request) { var notes []Note
for _, v := range noteStore { notes = append(notes, v) } w.Header().Set("Content-Type", "application/json") j, err := json.Marshal(notes) if err != nil { panic(err) } w.WriteHeader(http.StatusOK) w.Write(j) }
Here you iterate through the noteStore map, and the values are appended into a Note slice. By using the Marshal function of the json package, the Note slice is encoded as JSON.
ResponseWriter is used for writing response headers and bodies. Here the header is written using the WriteHeader method of ResponseWriter, and the response body is written using the Write method of ResponseWriter. When you call the API endpoint "/api/notes" with the HTTP Get method, you see the output in the format shown in Figure 4-4.
Figure 4-4. HTTP Get for the Notes resource
In Figure 4-4, you get the Note collection as JSON. Here the Postman REST API client tool is used to test the REST API example. Postman is a Chrome app that allows you to test your APIs. (Because it is a Chrome app, it runs only on the Chrome browser, however). With Postman, you can quickly construct HTTP requests to an API server, save them for later use, and analyze the responses sent by the API server. Postman is a very useful tool that can be used to test REST APIs without building a client application. To get more details, visit the Postman web site at www.getpostman.com.
Chapter 4 ■ GettinG Started with web development
Let’s have a look at the handler function for HTTP Post for creating a new Note resource: //HTTP Post - /api/notes
func PostNoteHandler(w http.ResponseWriter, r *http.Request) { var note Note
// Decode the incoming Note json
err := json.NewDecoder(r.Body).Decode(¬e) if err != nil { panic(err) } note.CreatedOn = time.Now() id++ k := strconv.Itoa(id) noteStore[k] = note j, err := json.Marshal(note) if err != nil { panic(err) } w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusCreated) w.Write(j) }
A pointer to the http.Request object is used to get information about HTTP Request. Here the incoming JSON data is accessed from Request.Body and decoded into the Note resource using the json package. The NewDecoder function creates a Decoder object, and its Decode method decodes the JSON string into the given type (the Note type in this example). The id variable is incremented to generate a key value for the noteStore map. The string type is used as the key for the noteStore map, so the int type is converted to string using the strconv.Itoa function. The new Note resource is added into the noteStore map with the key created with the id variable. Finally, the response is sent as JSON data for the newly created Note resource with the appropriate response header back to the HTTP client. json.Marshal is used to convert the Note object into JSON data.
Figure 4-5 shows testing of HTTP Post for the resource "/api/nodes". You see the newly created resource in the body with the HTTP status code 201 that represents the HTTP status "Created".
Chapter 4 ■ GettinG Started with web development
The endpoint "/api/notes/{id}" is used for the Note resource HTTP Put and HTTP Delete operations. In this example, the value of id is used as the key of the noteStore map. To retrieve this value from the request object, mux.Vars() is called:
vars := mux.Vars(r) k := vars["id"]
The Vars function of the mux package returns the route variables for the current request. With a route value of id, the Note object is retrieved from the noteStore map, and the value of CreatedOn is copied to the Note object.
Here the existing Note object is removed and added into the noteStore map for the sake of update functionality. Everything else is implemented the same way as the HTTP Post operation:
//HTTP Put - /api/notes/{id}
func PutNoteHandler(w http.ResponseWriter, r *http.Request) { var err error
vars := mux.Vars(r) k := vars["id"] var noteToUpd Note
// Decode the incoming Note json
err = json.NewDecoder(r.Body).Decode(¬eToUpd) if err != nil {
panic(err) }
Chapter 4 ■ GettinG Started with web development
if note, ok := noteStore[k]; ok { noteToUpd.CreatedOn = note.CreatedOn
//delete existing item and add the updated item delete(noteStore, k)
noteStore[k] = noteToUpd } else {
log.Printf("Could not find key of Note %s to delete", k) }
w.WriteHeader(http.StatusNoContent) }
Similar to the HTTP Put operation for the Note resource, the route value of id is taken and the Note object is removed from the noteStore map by using the key value from the route variable id:
//HTTP Delete - /api/notes/{id}
func DeleteNoteHandler(w http.ResponseWriter, r *http.Request) { vars := mux.Vars(r)
k := vars["id"] // Remove from Store
if _, ok := noteStore[k]; ok { //delete existing item delete(noteStore, k) } else {
log.Printf("Could not find key of Note %s to delete", k) }
w.WriteHeader(http.StatusNoContent) }
This example application demonstrated the fundamental concepts for building RESTful APIs in Go by using its standard library package net/http and the mux third-party package.
Summary
Go is a great technology stack for building web-based, back-end systems; and is especially excellent for building RESTful APIs. The net/http package from the standard library provides the fundamental blocks for building web applications in Go. The Go philosophy discourages using bigger frameworks for building web applications; it encourages the use of the net/http package as the fundamental block to use third-party packages and your own packages to extend the functionalities provided by the net/http package.
The http package has two major components for processing HTTP requests: http.ServeMux and http.Handler. http.ServeMux is a request multiplexor that compares incoming HTTP requests against a list of predefined URI resources and calls the associated handler for the resource requested by HTTP clients. Handlers are responsible for writing response headers and bodies. By using third-party packages such as mux, you can extend the capabilities of http.ServeMux.
The final section of this chapter explored the fundamental pieces of web development in Go by showing the development of a JSON-based RESTful API.