之前写过一篇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,"非法请求") } 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,64) boolFinish,_ := strconv.ParseBool(finish) _,err := models.FinishTodo(intId,!boolFinish) if err != nil { showError(w,"完成Todo失败") return } http.Redirect(w,http.StatusSeeOther) return } } func DeleteTodo(w http.ResponseWriter,"非法请求") } else { id := r.FormValue("id") intId,64) _,err := models.DeleteTodo(intId) if err != nil { showError(w,"删除失败") return } http.Redirect(w,http.StatusSeeOther) return } } func EditTodo(w http.ResponseWriter,r *http.Request) { if r.Method == "GET" { // 显示edit页面 // 本可以将title内容提交至此,但url将会异常难看,还是根据id查询吧 id := r.FormValue("id") intId,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"),64) title := r.FormValue("title") res,err := models.EditTodo(id,title) if err != nil || res <= 0 { showError(w,"修改失败") return } http.Redirect(w,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,"./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,"./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,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 } func GetTodoTitle(todoId int64) (string,"./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,err := db.Prepare("UPDATE todo SET title=? WHERE id=?") defer stmt.Close() if err != nil { return 0,id) if err != nil { return 0,nil }
至于view,代码就比较多了,不再贴出来了。
代码下载地址:http://download.csdn.net/detail/luck_apple/8436475 (0资源分)