設計API回傳/接收JSON


Posted by Nacho on 2020-04-01

實作目標

延續之前的寫的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的功能,因為概念與新增和讀取是相似的,所以不多做紀錄。
完整範例參考

筆記參考
Creating a RESTful API With Golang


#Go JSON #GO marshal #RESTful API







Related Posts

自動化測試 x Puppeteer - 玩偶QA參一咖 Day04

自動化測試 x Puppeteer - 玩偶QA參一咖 Day04

[FE102] 前端必備:JavaScript (下)

[FE102] 前端必備:JavaScript (下)

[JavaScript] JavaScript 入門

[JavaScript] JavaScript 入門


Comments