195 lines
4.9 KiB
Go
195 lines
4.9 KiB
Go
package main
|
||
|
||
import (
|
||
"encoding/json"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"os"
|
||
"os/signal"
|
||
"strconv"
|
||
)
|
||
|
||
// 增加额外的handler让客户端可以创建,读取,更新和删除数据库记录。
|
||
// 例如,一个形如 /update?item=socks&price=6
|
||
// 的请求会更新库存清单里一个货品的价格并且当这个货品不存在或价格无效时返回一个错误值。
|
||
//(注意:这个修改会引入变量同时更新的问题)
|
||
|
||
// TODO:
|
||
// 1. 将数据保存到本地json文件
|
||
// 2. 开启web服务时读取本地json文件
|
||
// 3. 读取数据时,如果本地json文件不存在,则从默认值获取数据
|
||
// 4. 实现web服务器
|
||
// 5. 实现增删改查
|
||
// 6. 关闭服务器时保存数据到本地json文件
|
||
type dollars float32
|
||
|
||
func (d dollars) String() string {
|
||
return fmt.Sprintf("$%.2f", d)
|
||
}
|
||
|
||
type database map[string]dollars
|
||
|
||
// list 货物清单
|
||
func (db database) list(w http.ResponseWriter, req *http.Request) {
|
||
for item, price := range db {
|
||
fmt.Fprintf(w, "%s: %s\n", item, price)
|
||
}
|
||
}
|
||
|
||
// price 价格
|
||
func (db database) price(w http.ResponseWriter, req *http.Request) {
|
||
item := req.URL.Query().Get("item")
|
||
price, ok := db[item]
|
||
if !ok {
|
||
w.WriteHeader(http.StatusNotFound) // 404
|
||
fmt.Fprintf(w, "no such item: %q\n", item)
|
||
return
|
||
}
|
||
fmt.Fprintf(w, "%s\n", price)
|
||
}
|
||
|
||
// create 创建
|
||
func (db database) create(w http.ResponseWriter, req *http.Request) {
|
||
item := req.URL.Query().Get("item")
|
||
price := req.URL.Query().Get("price")
|
||
_, ok := db[item]
|
||
if ok {
|
||
w.WriteHeader(http.StatusNotFound) // 404
|
||
fmt.Fprintf(w, "item 已经存在: %q\n", item)
|
||
return
|
||
}
|
||
// string to float
|
||
priceF, err := strconv.ParseFloat(price, 32)
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusNotFound) // 404
|
||
fmt.Fprintf(w, "price is invalid: %q\n", price)
|
||
return
|
||
}
|
||
db[item] = dollars(priceF)
|
||
fmt.Fprintf(w, "create item: %q\n", item)
|
||
fmt.Fprintf(os.Stdout, "item create: %q\n", db)
|
||
}
|
||
|
||
// update 更新
|
||
func (db database) update(w http.ResponseWriter, req *http.Request) {
|
||
item := req.URL.Query().Get("item")
|
||
price := req.URL.Query().Get("price")
|
||
_, ok := db[item]
|
||
if !ok {
|
||
w.WriteHeader(http.StatusNotFound) // 404
|
||
fmt.Fprintf(w, "item 不存在: %q\n", item)
|
||
return
|
||
}
|
||
// string to float
|
||
priceF, err := strconv.ParseFloat(price, 32)
|
||
if err != nil {
|
||
w.WriteHeader(http.StatusNotFound) // 404
|
||
fmt.Fprintf(w, "price is invalid: %q\n", price)
|
||
return
|
||
}
|
||
db[item] = dollars(priceF)
|
||
fmt.Fprintf(w, "update item: %q\n", item)
|
||
fmt.Fprintf(os.Stdout, "item update: %q\n", db)
|
||
}
|
||
|
||
// delete 删除
|
||
func (db database) delete(w http.ResponseWriter, req *http.Request) {
|
||
item := req.URL.Query().Get("item")
|
||
_, ok := db[item]
|
||
if !ok {
|
||
w.WriteHeader(http.StatusNotFound) // 404
|
||
fmt.Fprintf(w, "item 不存在: %q\n", item)
|
||
return
|
||
}
|
||
delete(db, item)
|
||
fmt.Fprintf(w, "delete item: %q\n", item)
|
||
fmt.Fprintf(os.Stdout, "item delete: %q\n", db)
|
||
}
|
||
|
||
// 默认数据
|
||
var defaultDB database = database{"shoes": 50, "socks": 5}
|
||
|
||
// 数据库
|
||
var db database
|
||
|
||
func loadDB() error {
|
||
path := "/Users/dugulingping/code/Golang/Study/src/study/day9Interface/Practice/db.json"
|
||
// 打开文件
|
||
file, err := os.OpenFile(path, os.O_RDWR|os.O_CREATE, 0755)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
// 关闭文件
|
||
defer file.Close()
|
||
// 读取文件, 解析json文件
|
||
decoder := json.NewDecoder(file)
|
||
if err := decoder.Decode(&db); err != nil {
|
||
// 如果文件为空,则从默认值获取数据
|
||
if err == io.EOF {
|
||
// 从默认值获取数据
|
||
fmt.Fprintln(os.Stdout, "从默认值获取数据")
|
||
db = defaultDB
|
||
return nil
|
||
} else {
|
||
// 如果文件不为空,但是解析失败,则返回错误
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// saveDB 保存数据到本地json文件
|
||
func saveDB() {
|
||
path := "/Users/dugulingping/code/Golang/Study/src/study/day9Interface/Practice/db.json"
|
||
// 打开文件
|
||
file, err := os.OpenFile(path, os.O_RDWR, 0755)
|
||
if err != nil {
|
||
panic(err)
|
||
}
|
||
// 关闭文件
|
||
defer file.Close()
|
||
// 清空文件
|
||
file.Truncate(0)
|
||
// 从头开始写入
|
||
file.Seek(0, 0)
|
||
// 编码json文件
|
||
encoder := json.NewEncoder(file)
|
||
if err := encoder.Encode(db); err != nil {
|
||
panic(err)
|
||
}
|
||
fmt.Fprintf(os.Stdout, "save db: %q", db)
|
||
}
|
||
func main() {
|
||
// 从本地json文件读取数据
|
||
err := loadDB()
|
||
if err != nil {
|
||
fmt.Fprintf(os.Stdout, "loadDB err: %q", err)
|
||
}
|
||
// 创建通道
|
||
c := make(chan os.Signal)
|
||
// 监听指定信号 ctrl+c kill
|
||
signal.Notify(c, os.Interrupt)
|
||
// 启动一个协程
|
||
// 目的是当接收到指定信号时,执行保存数据库
|
||
go func() {
|
||
select {
|
||
case sig := <-c:
|
||
fmt.Printf("Got %s signal. Aborting...\n", sig)
|
||
// 保存数据库
|
||
saveDB()
|
||
// 退出程序
|
||
os.Exit(1)
|
||
}
|
||
}()
|
||
// 创建路由
|
||
http.HandleFunc("/list", db.list)
|
||
http.HandleFunc("/price", db.price)
|
||
http.HandleFunc("/create", db.create)
|
||
http.HandleFunc("/update", db.update)
|
||
http.HandleFunc("/delete", db.delete)
|
||
// 监听端口
|
||
http.ListenAndServe("localhost:8000", nil)
|
||
|
||
}
|