Thursday, June 26, 2014

Building a JSON REST API in Go

Last post we saw how simple it is to expose an html website with Go. 
This time we will learn in very easy steps how to transform it into a JSON REST API

we are going to use the last piece of code from the prior tutorial as our starting point.

Go has a baked in json encoding library that makes it very easy to do this task. 
All we need to do is add a new function that will return the closures to handle the request (you will need to add "encoding/json" in your imports):

func ApiHandleCategory(c Category) func (w http.ResponseWriter, r *http.Request) {
return func (w http.ResponseWriter, r *http.Request) {
j, err := json.Marshal(c)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
}
}

Encoding our data into json is magically done by json.Marshal function. If all goes well you will receive the json representation of the data in a byte buffer which you can write to the response writer.
Finaly we expose the REST API with the url scheme /api/{category}
For example /api/compact


for _, cat := range inventory {
http.HandleFunc("/" + cat.Title, HandleCategory(cat))
http.HandleFunc("/api/" + cat.Title, ApiHandleCategory(cat))

}

We are done. we have (albeit a read only) transformed our web site into a REST API with 9 lines of code.
Here is the full code:


package main

import (
    "fmt"
    "net/http"
"html/template"
"os"
"encoding/json"
)

type Car struct {
Name string
Count int
}

type Category struct {
Title string
Cars []Car
}


func HandleUnknown(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusNotFound);
fmt.Fprint(w, "Hello\n")
fmt.Fprint(w, "This url does not exist")            
}

func HandleCategory(c Category) func (w http.ResponseWriter, r *http.Request) {
return func (w http.ResponseWriter, r *http.Request) {
tmpl, err := template.ParseFiles("template.html")
if err != nil {
fmt.Println("error - did not load template", err);
os.Exit(-2);
}
tmpl.Execute(w, c)
}
}

func ApiHandleCategory(c Category) func (w http.ResponseWriter, r *http.Request) {
return func (w http.ResponseWriter, r *http.Request) {
j, err := json.Marshal(c)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
} else {
w.Header().Set("Content-Type", "application/json")
w.Write(j)
}
}
}

func main() {
inventory := []Category { 
Category { "compact", []Car { Car { "Hyundai i30", 3 }, Car { "Volkswagen Golf", 1 }, Car { "Chevrolet Cruze", 2 } } },
Category { "saloon", []Car { Car { "Toyota Camry", 3 }, Car { "Volkswagen Passat", 1 }, Car { "Audi A6", 2 } } },
Category { "sports", []Car { Car { "Chverolet Corvette", 3 }, Car { "Ferrari F12", 1 }, Car { "Porsche 911", 2 } } },
}

for _, cat := range inventory {
http.HandleFunc("/" + cat.Title, HandleCategory(cat))
http.HandleFunc("/api/" + cat.Title, ApiHandleCategory(cat))
}

    http.HandleFunc("/", HandleUnknown)
    http.ListenAndServe("localhost:8080", nil)

}

No comments:

Post a Comment