之前写过一篇todo实例,是使用python的tornado框架实现的。地址:http://blog.csdn.net/luck_apple/article/details/8814091
最近go更新到了1.4版本,正好也研究来玩玩。
看了几天go基础,然后想找个例子练练手,就想起来遇险写过的tornado的todo了,
直接拿来用go重写一遍,本次没有使用web框架,使用go原生API实现。
go语言基础可以看看电子书:http://download.csdn.net/detail/luck_apple/8412345 (0资源分)
go版本1.4,再看下我的go env:
先看看最终效果吧:
ok,就是这个样子。
上次有人抱怨我没有把数据库表结构亮出来,这次索性使用sqlite数据库,表结构真心很简单:
CREATE TABLE todo (id integer PRIMARY KEY,titletextNOT NULL,finishbooleanNOT NULLDEFAULT false)
再看下工程目录结构:
咱虽然没有使用mvc的web框架,但也得有个mvc的样子,mode,view和controller还是要分分清楚的。
入口为main.go(负责创建web server和路由):
package main
import (
"log"
"net/http"
"todo/controllers"
)
func main() {
// 静态资源服务
http.Handle("/public/", http.FileServer(http.Dir("./")))
// 路由
http.HandleFunc("/new", controllers.NewTodo)
http.HandleFunc("/edit", controllers.EditTodo)
http.HandleFunc("/finish", controllers.FinishTodo)
http.HandleFunc("/delete", controllers.DeleteTodo)
http.HandleFunc("/", controllers.Index)
err := http.ListenAndServe(":3000", nil)
if err != nil {
log.Fatal("ListenAndServer error:", err)
}
}
再看看TodoController.go:
package controllers
import (
"html/template"
"net/http"
"strconv"
"todo/models"
)
func Index(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
showError(w, "异常", "非法请求,服务器无法响应")
} else {
if r.URL.Path == "/" {
todos, err := models.QueryAll()
if err != nil {
showError(w, "异常", "查询异常")
return
}
t, err := template.ParseFiles("views/index.html")
if err != nil {
showError(w, "异常", "页面渲染异常")
return
}
data := make(map[string][]models.Todo)
data["TodoList"] = todos
t.Execute(w, data)
} else {
// 404页面,路由不到的都会到这里
showError(w, "404", "页面不存在")
}
}
}
func NewTodo(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
showError(w, "异常", "非法请求")
} else {
title := r.FormValue("title")
id, err := models.InsertTodo(title)
if err != nil || id <= 0 {
showError(w, "异常", "插入数据异常")
return
}
// 重定向到主界面
http.Redirect(w, r, "/", http.StatusSeeOther)
// 没有return,没有效果,重定向不过去
return
}
}
func FinishTodo(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
showError(w, "异常", "非法请求")
} else {
// 获取表单参数,也可以这么写
// r.ParseForm()
// id := r.Form["id"]
id := r.FormValue("id")
finish := r.FormValue("finish")
// FormValue取到的数据都为string类型,将id转为int64类型
// strconv.ParseInt(id, 10, 64) 10意思为10进制,64意思为64位
intId, _ := strconv.ParseInt(id, 10, 64)
boolFinish, _ := strconv.ParseBool(finish)
_, err := models.FinishTodo(intId, !boolFinish)
if err != nil {
showError(w, "异常", "完成Todo失败")
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
}
func DeleteTodo(w http.ResponseWriter, r *http.Request) {
if r.Method != "GET" {
showError(w, "异常", "非法请求")
} else {
id := r.FormValue("id")
intId, _ := strconv.ParseInt(id, 10, 64)
_, err := models.DeleteTodo(intId)
if err != nil {
showError(w, "异常", "删除失败")
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
}
func EditTodo(w http.ResponseWriter, r *http.Request) {
if r.Method == "GET" {
// 显示edit页面
// 本可以将title内容提交至此,但url将会异常难看,还是根据id查询吧
id := r.FormValue("id")
intId, _ := strconv.ParseInt(id, 10, 64)
title, err := models.GetTodoTitle(intId)
if err != nil {
showError(w, "异常", "查询Todo内容失败")
return
}
t, _ := template.ParseFiles("views/edit.html")
data := make(map[string]string)
data["Id"] = id
data["Title"] = title
t.Execute(w, data)
} else if r.Method == "POST" {
// edit后的数据post提交至此处
id, _ := strconv.ParseInt(r.FormValue("id"), 10, 64)
title := r.FormValue("title")
res, err := models.EditTodo(id, title)
if err != nil || res <= 0 {
showError(w, "异常", "修改失败")
return
}
http.Redirect(w, r, "/", http.StatusSeeOther)
return
}
}
// 错误处理
func showError(w http.ResponseWriter, title string, message string) {
t, _ := template.ParseFiles("views/error.html")
data := make(map[string]string)
data["title"] = title
data["message"] = message
t.Execute(w, data)
}
Mac的用户可以试试一款极好的sqlite数据库管理工具:http://download.csdn.net/detail/luck_apple/8436489 (0资源分)
再看看我们的model,TodoModel.go:
package models
import (
"database/sql"
_ "github.com/mattn/go-sqlite3"
)
// 为开发方便,使用sqlite数据库
type Todo struct {
Id int64
Title string
Finish bool
}
func InsertTodo(title string) (int64, error) {
// 数据库path是相对路径(相对于main.go)
db, err := sql.Open("sqlite3", "./data/data.db")
// 函数代码执行完后关闭数据库,这是个好习惯,我爱defer
defer db.Close()
if err != nil {
return -1, err
}
stmt, err := db.Prepare("INSERT INTO todo(title, finish) VALUES(?, ?)")
defer stmt.Close()
if err != nil {
return -1, err
}
res, err := stmt.Exec(title, false)
if err != nil {
return -1, err
}
return res.LastInsertId()
}
func QueryAll() ([]Todo, error) {
db, err := sql.Open("sqlite3", "./data/data.db")
defer db.Close()
if err != nil {
return nil, err
}
rows, err := db.Query("SELECT * FROM todo")
defer rows.Close()
if err != nil {
return nil, err
}
var todos []Todo
for rows.Next() {
var id int64
var title string
var finish bool
err = rows.Scan(&id, &title, &finish)
if err != nil {
return nil, err
}
todo := Todo{id, title, finish}
todos = append(todos, todo)
}
return todos, nil
}
func FinishTodo(todoId int64, finish bool) (int64, error) {
db, err := sql.Open("sqlite3", "./data/data.db")
defer db.Close()
if err != nil {
return 0, err
}
stmt, err := db.Prepare("UPDATE todo SET finish=? WHERE id=?")
defer stmt.Close()
if err != nil {
return 0, nil
}
res, err := stmt.Exec(finish, todoId)
if err != nil {
return 0, nil
}
affect, err := res.RowsAffected()
if err != nil {
return 0, nil
}
return affect, nil
}
func DeleteTodo(todoId int64) (int64, error) {
db, err := sql.Open("sqlite3", "./data/data.db")
defer db.Close()
if err != nil {
return 0, err
}
stmt, err := db.Prepare("DELETE FROM todo WHERE id=?")
if err != nil {
return 0, err
}
res, err := stmt.Exec(todoId)
if err != nil {
return 0, nil
}
affect, err := res.RowsAffected()
if err != nil {
return 0, nil
}
return affect, nil
}
func GetTodoTitle(todoId int64) (string, error) {
db, err := sql.Open("sqlite3", "./data/data.db")
defer db.Close()
if err != nil {
return "", err
}
// 只查询一行数据
row := db.QueryRow("SELECT title FROM todo WHERE id=?", todoId)
var title string
e := row.Scan(&title)
if e != nil {
return "", e
}
return title, nil
}
func EditTodo(id int64, title string) (int64, error) {
db, err := sql.Open("sqlite3", "./data/data.db")
defer db.Close()
if err != nil {
return 0, err
}
stmt, err := db.Prepare("UPDATE todo SET title=? WHERE id=?")
defer stmt.Close()
if err != nil {
return 0, nil
}
res, err := stmt.Exec(title, id)
if err != nil {
return 0, nil
}
affect, err := res.RowsAffected()
if err != nil {
return 0, nil
}
return affect, nil
}
代码下载地址:http://download.csdn.net/detail/luck_apple/8436475 (0资源分)