第三期 · 使用 Vue 3.1 + TailWind.CSS + Axios + Golang + Sqlite3 實現簡單評論機制
C:.
│ comment.json
│ go.mod
│ go.sum
│ main.go
│
├───data
│ data.db
│
└───lib
├───http
│ server.go
│
├───mysql
└───sqlite
sq3_comment.go
sq3_init.go
sq3_users.go
我們把訊息JSON格式擬定出來
[
{
"id": 1,
"uid": 1001,
"name": "小王",
"text": "看起來很好玩的樣子。",
"pid": 100,
"date": 1665908807784
}
]
JSON 轉GO,JSON轉GO程式碼, go json解析 (sojson.com)
type AutoGenerated []struct {
ID int `json:"id"`
UID int `json:"uid"`
Name string `json:"name"`
Text string `json:"text"`
Pid int `json:"pid"`
Date int64 `json:"date"`
}
Windows 如果使用 Go 語言使用 sqlite3 時,需要gcd來編譯sqlite3模組相關c程式碼。
解決方法:安裝tdm64-gcc-9.2.0.exe, https://jmeubank.github.io/tdm-gcc/download/
init() 初始化函數獲取main執行目錄,並按作業系統連線檔案位置,讀取檔案。
package sq3_vue
import (
"database/sql"
"os"
"path"
_ "github.com/mattn/go-sqlite3"
)
var db *sql.DB
func init() {
p, err := os.Getwd()
checkError(err)
db, err = sql.Open("sqlite3", path.Join(p, "data/data.db"))
checkError(err)
}
func checkError(err error) {
if err != nil {
panic(err)
}
}
為具體的資料庫處理邏輯,插入返回comment的json位元組切片 {}
,查詢返回comment陣列的json位元組切片 [{},{},{}]
。
*sql.DB 是Go標準庫規定的介面,方便操作。
stmt、rows 需要 defer close()
package sq3_vue
import (
"encoding/json"
"fmt"
"time"
)
type Comment struct {
ID int `json:"id"`
UID int `json:"uid"`
Name string `json:"name"`
Text string `json:"text"`
Pid int `json:"pid"`
Date int64 `json:"date"`
}
const insertStmt = `
INSERT INTO comments(uid,text,pid,date) values(?,?,?,?)
`
const lastedStmt = `
select id,uid,text,pid,date,name from comments join users using(uid) where id = ?
`
func (Comment) InsertComment(uid, pid int64, text string) (json_ []byte, err error) {
stmt, err := db.Prepare(insertStmt)
checkError(err)
defer stmt.Close()
res, err := stmt.Exec(uid, text, pid, time.Now().UnixMilli())
checkError(err)
n, err := res.RowsAffected()
checkError(err)
if n == 0 {
return nil, fmt.Errorf("插入失敗")
}
n, err = res.LastInsertId()
checkError(err)
stmt, err = db.Prepare(lastedStmt)
checkError(err)
defer stmt.Close()
rows, err := stmt.Query(n)
checkError(err)
defer rows.Close()
rows.Next()
var c Comment
rows.Scan(&c.ID, &c.UID, &c.Text, &c.Pid, &c.Date, &c.Name)
checkError(err)
json_, err = json.Marshal(c)
checkError(err)
return json_, nil
}
const deleteStmt = `
delete from comments where id = ?
`
func (Comment) DeleteComment(id int64) error {
stmt, err := db.Prepare(deleteStmt)
checkError(err)
defer stmt.Close()
res, err := stmt.Exec(id)
checkError(err)
n, err := res.RowsAffected()
checkError(err)
if n == 0 {
return fmt.Errorf("刪除失敗")
}
return nil
}
const queryStmt = `
select id,uid,text,pid,date,name from comments join users using(uid) where pid = ?
`
func (Comment) QueryComment(pid int64) (json_ []byte, err error) {
var res []Comment
stmt, err := db.Prepare(queryStmt)
checkError(err)
defer stmt.Close()
rows, err := stmt.Query(pid)
checkError(err)
defer rows.Close()
for rows.Next() {
var c Comment
err = rows.Scan(&c.ID, &c.UID, &c.Text, &c.Pid, &c.Date, &c.Name)
checkError(err)
res = append(res, c)
}
json_, err = json.Marshal(res)
checkError(err)
return
}
我們分別判斷請求方法,要求刪除和插入只能用post請求,查詢只能用get請求。使用r.ParseForm() 處理表單。
r.Form["uid"]
本質上拿到的字串陣列,需要進行顯式型別轉換。
db "wolflong.com/vue_comment/lib/sqlite"
引入了前面寫的資料庫處理包。因為考慮到不一定要用 sqlite,未來可能會使用 mysql、mongoDB。目前已經強耦合了,即當前http伺服器的實現跟sq3_vue包緊密相關,考慮用介面降低耦合程度。
type comment interface {
QueryComment(pid int64) (json_ []byte, err error)
InsertComment(uid, pid int64, text string) (json_ []byte, err error)
DeleteComment(id int64) error
}
var c comment = db.Comment{}
我們將資料庫行為接收者指派為Comment型別,當該型別實現了三個對應函數簽名的方法就實現了comment介面。此時我們建立一個空Comment型別賦值給comment介面變數。那麼其他資料庫邏輯處理包只要提供實現comment介面的型別物件就好了。換什麼資料庫也影響不到當前HTTP的處理邏輯。
package server
import (
"fmt"
"log"
"net/http"
"strconv"
db "wolflong.com/vue_comment/lib/sqlite"
)
type comment interface {
QueryComment(pid int64) (json_ []byte, err error)
InsertComment(uid, pid int64, text string) (json_ []byte, err error)
DeleteComment(id int64) error
}
var c comment = db.Comment{}
func checkError(err error) {
if err != nil {
panic(err)
}
}
func insertComment(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
fmt.Fprintf(w, "Only POST Method")
return
}
r.ParseForm()
fmt.Println(r.Form)
// ^ 簡單實現,有待提高健壯性
uid, err := strconv.Atoi(r.Form["uid"][0])
checkError(err)
pid, err := strconv.Atoi(r.Form["pid"][0])
checkError(err)
text := r.Form["text"][0]
inserted, err := c.InsertComment(int64(uid), int64(pid), text)
if err != nil {
fmt.Fprintf(w, "Error Insert")
return
}
fmt.Fprint(w, string(inserted))
}
func deleteComment(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
fmt.Fprintf(w, "Only POST Method")
return
}
r.ParseForm()
fmt.Println(r.Form)
id, err := strconv.Atoi(r.Form["id"][0])
checkError(err)
err = c.DeleteComment(int64(id))
if err != nil {
fmt.Fprintf(w, "Error Delete")
return
}
fmt.Fprintf(w, "Success Delete")
}
func queryComment(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
fmt.Fprintf(w, "Only GET Method")
return
}
r.ParseForm()
fmt.Println(r.Form)
pid, err := strconv.Atoi(r.Form["pid"][0])
checkError(err)
json, err := c.QueryComment(int64(pid))
if err != nil {
fmt.Fprintf(w, "Error Delete")
return
}
fmt.Fprint(w, string(json))
}
func StartServer() {
http.HandleFunc("/insertComment", insertComment)
http.HandleFunc("/deleteComment", deleteComment)
http.HandleFunc("/queryComment", queryComment)
err := http.ListenAndServe(":1314", nil)
if err != nil {
log.Fatal("ListenAndServe: ", err)
}
}
package main
import (
"fmt"
http "wolflong.com/vue_comment/lib/http"
)
func main() {
fmt.Println("2022年10月16日 https://cnblogs.com/linxiaoxu")
http.StartServer()
}
SQLite Join | 菜鳥教學 (runoob.com)
使用 SQLite 資料庫 - 使用 Golang 打造 Web 應用程式 (gitbook.io)
mattn/go-sqlite3: sqlite3 driver for go using database/sql (github.com)
sqlite3 package - github.com/mattn/go-sqlite3 - Go Packages
go-sqlite3/simple.go at master · mattn/go-sqlite3 (github.com)
05.3. 使用 SQLite 資料庫 | 第五章. 存取資料庫 |《Go Web 程式設計》| Go 技術論壇 (learnku.com)
04.1. 處理表單的輸入 | 第四章. 表單 |《Go Web 程式設計》| Go 技術論壇 (learnku.com)