之前写过一篇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中的对应方法
再看看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) }
数据库使用了sqlite3数据库,使用github.com/mattn/go-sqlite3软件包
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 }
至于view,代码就比较多了,不再贴出来了。
代码下载地址:http://download.csdn.net/detail/luck_apple/8436475 (0资源分)
版权声明:本文为博主原创文章,未经博主允许不得转载。