實作目標
延續之前的寫的Simple RESTful API,加入解析JSON的功能使API可以存取JSON資料。
the small wiki of JSON(參考)
- JSON 的全名叫做 JavaScript Object Notation
- 是一個存取與交換資料的輕量格式(輕量大概是跟XML比較出來的)
- 通常被Server用來最為傳給網頁的資料
開始
階段一: 專案基礎建設
先進行專案的基礎建設,開一個新專案叫simpleJson,新增一個檔案叫simplejson.go,並導入了套件gorilla/mux。用這行指令$ go get -u github.com/gorilla/mux
取得這個套件。
package main
import(
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
func home(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "welcome home!")
}
func main(){
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", home)
log.Fatal(http.ListenAndServe(":8080", router))
}
階段二: 取得JSON格式的資料
首先,宣告一個叫event的結構,json格式的宣告附在value之後。宣告一個allEvents的slice用來存取多筆event,並在events初始化兩筆event,另外,還需要匯入encoding套件裡的json函示庫,之後要透過它來解析json資料。
注意: 在struct的宣告中key必須是大寫開頭,否則會拋出錯誤(切身經驗),但可以在json內將key轉換成小寫!! 原因
import(
"encoding/json"
...
)
type event struct {
Id string `json:"id"`
Title string `json:"title"`
Desc string `json:"desc"`
}
type allEvents []event
var events = allEvents{ // 加入一些events
{
Id: "1", Title: "test1", Desc: "this is the first test",
},
{
Id: "2", Title: "test2", Desc: "this is the second test",
},
}
加入一個getAllEvents的func,內容是將回傳值透過json加入解析後的events資料。打開Terminal使用curl -X GET localhost:8080/events
可以看到回傳值是一串JSON解析後的格式,如果與fmt.Println印出來的結果相比應該會發現JSON提供完整的key/value。
func getAllEvents(w http.ResponseWriter, r *http.Request){
json.NewEncoder(w).Encode(events)
fmt.Println("call all events api")
// fmt.Println(events)
}
func main(){
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/", home)
router.HandleFunc("/events", getAllEvents).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
階段三: 存取JSON資料
新增一筆event,這裡使用到io套件的ioutil功能,ioutil.ReadAll(r.Body)讀取API傳來的HTTP body。Go透過 json.Unmarshal 將JSON字串轉成 struct(json.marshal則相反),所以我將reqBody的值轉換後指給newEvent,並將它加入events中,最後回傳成功結果給client。打開Terminal用curl -X POST -d '{"id":"3","title":"test3","desc":"This is the third test"}' localhost:8080/event
import(
"io/ioutil"
...
)
func createEvent(w http.ResponseWriter, r *http.Request){
var newEvent event
reqBody, _ := ioutil.ReadAll(r.Body)
// 轉換成 event struct
json.Unmarshal(reqBody, &newEvent)
events = append(events, newEvent)
// 201 created status code
w.WriteHeader(http.StatusCreated)
json.NewEncoder(w).Encode(newEvent)
}
func main(){
..
router.HandleFunc("/event", createEvent).Methods("POST")
..
}
取得其中一筆event,假設我在Terminal輸入curl -X GET localhost:8080/events/1
,mux幫我解析這串url,若是發現符合events/{id}的格式,mux.Vars(r)["id"]幫我把id的值取出給路徑變數,接著在迴圈中找出有等於這個id的event,並回傳給client。
func getOneEvent(w http.ResponseWriter, r *http.Request) {
// 宣告路徑變數,解析 url取出 id 的值
eventId := mux.Vars(r)["id"]
// loop 找出對應 id
for _, singleEvent := range events {
if singleEvent.Id == eventId {
json.NewEncoder(w).Encode(singleEvent)
}
}
}
func main(){
..
router.HandleFunc("/events/{id}", getOneEvent).Methods("GET")
..
}
另外,還有編輯跟刪除event的功能,因為概念與新增和讀取是相似的,所以不多做紀錄。
完整範例參考