หัด Golang part 4
RESTful API กับ gorilla/mux pacakge
ความเดิม
เราสามารถเชื่อมต่อ Golang กับฐานข้อมูล MS SQL Server มาถึง part นี้เราจะเขียน RESTful API เพื่อขอ เพิ่ม แก้ไขและลบ resource ซึ่งเก็บไว้ในฐานข้อมูล bookdb
Create Database
สร้างฐานข้อมูลชื่อ bookdb พร้อมกับกำหนดให้เข้าใจภาษาไทย
CREATE DATABASE bookdbCOLLATE Thai_CI_AS
Create Table
สร้างตารางชื่อ book ไว้เก็บหนังสือ
CREATE TABLE book( id INT NOT NULL IDENTITY PRIMARY KEY, isbn VARCHAR(20) NOT NULL, name VARCHAR(100), authorName VARCHAR(100), year CHAR(4), totalPage INT,)
เพิ่มหนังสือเล่มที่หนึ่ง
INSERT INTO book(isbn, name, authorName, year, totalPage)VALUES('978-616-08-2539-4', 'พัฒนาเว็บแอปพลิเคชั่นด้วย JavaScript', 'จตุรพัชร์ พัฒนทรงศิวิไล', '2529', 460)
เพิ่มหนังสือเล่มที่สอง
INSERT INTO book(isbn, name, authorName, year, totalPage)VALUES('978-616-93108-0-8', 'BIG DATA SERIES I, Introduction to a Big Data Project', 'ดร.อสมา กุลวานิชไชยนันท์', '2561', 247)
ผล
gorilla/mux pacakge
เราต้องการ router เพื่อจัดการกับ URI ต่างๆเหล่านี้
- GET /books/{id}
- PUT /books/{id}
- DELETE /books/{id}
โดย Golang เสนอให้จัดการที่ r *http.Request ซึ่งเพื่อนๆได้เห็นผ่านตามาบ้างแล้ว แต่การใช้ r.URL.Query นั้นลำบากและต้องเขียนโค้ดเยอะ เราพบว่ามี package ชื่อ gorilla/mux เป็นพระเอกของเรื่องนี้ครับ
ดึงมันลงมา
go get -u github.com/gorilla/mux
ด้วยเหตุนี้เราสามารถใช้ gorilla/mux มาจัดการ router ได้
router := mux.NewRouter()router.HandleFunc("/books", getAllBooks).Methods("GET")
จากนั้นระบุไปใน http
http.ListenAndServe(":8080", router)
Update Database Connection String
เดิม connection string ที่สร้างไว้ใน part ก่อนไม่ได้ระบุฐานข้อมูลไว้ เมื่อเรามีฐานข้อมูลชื่อ bookdb แล้วก็ต้องเพิ่มส่วนนี้เข้าไปด้วย
ส่วนที่ประกาศตัวแปร global ได้ว่า
var server = "localhost"var port = 1433var user = "sa"var password = "Password1234"var database = "bookdb"
จากนั้นแก้ไข connection string
connString := fmt.Sprintf("server=%s;user id=%s;password=%s;port=%d;database=%s", server, user, password, port, database)
Create db and err global variables
เนื่องจากฟังก์ชัน getAllBooks จำต้องใช้ตัวแปร db ที่ได้มาจากการ connection เพื่อ query ข้อมูลจากฐานข้อมูลมาใส่ไว้ใน struct ดังนั้นจึงต้องสร้างตัวแปร db และ err ในระดับ global
ส่วนที่ประกาศตัวแปร global ได้ว่า
var db *sql.DBvar err error
จากนั้นแก้ไขส่วน assignment คือเปลี่ยนจากเครื่องหมาย := เป็น = เฉยๆ
db, err = sql.Open("mssql", connString)
ด้วยเหตุนี้ฟังก์ชัน checkVersion เดิมที่รับ db เข้ามาเป็นพารามิเตอร์ก็ไม่จำเป็นแล้ว
func checkVersion() {
...
}
Update book struct
แน่นอนครับ เพราะตาราง book สัมพันธ์กับ book struct เมื่อมี id เพิ่มที่ตารางย่อมส่งผลให้ struct ต้องเพิ่ม id ด้วย ได้ว่า
type book struct { Id int `json:"id"` Isbn string `json:"isbn"` Name string `json:"name"` AuthorName string `json:"authorName"` Year string `json:"year"` TotalPage int `json:"totalPage"`}
getAllBooks Function
และแล้วก็มาถึงพระเอกของเรา ฟังก์ชัน getAllBooks ซึ่งจะต้องใช้ตัวแปร db ขอ query ไปยังฐานข้อมูลผ่านฟังก์ช้น Query
func (db *DB) Query(query string, args ...interface{}) (*Rows, error)
ฟังก์ชันนี้ให้ผลเป็น *Rows กับ error
ตัว *Rows นี้เมื่อทำงานเสร็จแล้วก็ต้อง Close ด้วยนะ (ห้ามลืม)
func getAllBooks(w http.ResponseWriter, r *http.Request) { rows, err := db.Query("SELECT * FROM book") if err != nil { log.Fatal("Database SELECT failed:", err.Error()) } defer rows.Close()...
มาถึงส่วนสุดท้าย เมื่อได้ข้อมูลมาแล้วก็ต้องใส่เข้าไปใน struct ก่อนจะนำไปพ่นออกเป็น JSON ต่อไป
var b bookerr := rows.Scan(&b.Id, &b.Isbn, &b.Name, &b.AuthorName, &b.Year, &b.TotalPage)...json.NewEncoder(w).Encode(books)
โค้ดทั้งหมด
rerun server แล้วดูผลลัพธ์
เอาล่ะครับ เอาให้ได้เท่านี้ก่อน ถ้าได้แล้วไม่ติดขัดเราค่อยมาต่อ POST, PUT และ DELETE กันนะ