golang todo实例

luck_apple · · 6714 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

之前写过一篇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资源分)


版权声明:本文为博主原创文章,未经博主允许不得转载。


有疑问加站长微信联系(非本文作者)

本文来自:CSDN博客

感谢作者:luck_apple

查看原文:golang todo实例

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

6714 次点击  
加入收藏 微博
4 回复  |  直到 2000-01-01 00:00:00
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传