使用Go語言和Vue.js的簡單Todo專案教程

banq發表於2016-10-07
這是一個使用Go語言結合Vue.js的簡單Todos專案,可以用來學習Go與Vue.js。

使用條件:
1.使用了Echo框架. Echo是類似Slim PHP 或 Lumen.的Go微框架,透過路由處理HTTP請求。
2.使用SQLite 資料庫。

開發步驟:
1. 下載echo和SQLite:
$ go get github.com/labstack/echo
$ go get github.com/mattn/go-sqlite3

2.建立專案目錄:
$ cd $GOPATH/src
$ mkdir go-echo-vue && cd go-echo-vue

3.建立todo.go檔案:

// todo.go
package main

import (
    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
)
<p class="indent">


建立main函式:

// todo.go
func main() { }
<p class="indent">


為了讓VueJS能夠和後端通訊,建立基本路由,首先建立Echo新例項,使用內建函式定義簡單路由規則。
在main函式中寫入這些規則:

// todo.go
func main() {
    // Create a new instance of Echo
    e := echo.New()

    e.GET("/tasks", func(c echo.Context) error { return c.JSON(200, "GET Tasks") })
    e.PUT("/tasks", func(c echo.Context) error { return c.JSON(200, "PUT Tasks") })
    e.DELETE("/tasks/:id", func(c echo.Context) error { return c.JSON(200, "DELETE Task "+c.Param("id")) })

    // Start as a web server
    e.Run(standard.New(":8000"))
}
<p class="indent">


啟動:
$ go build todo.go
$ ./todo

為了測試路由,使用Chrome外掛Postman.開啟Postman,指向localhost:8000,使用各種HTTP動詞測試/tasks

下面是初始化資料庫,指定檔名為storage.db:

// todo.go
package main
import (
    "database/sql"

    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    _ "github.com/mattn/go-sqlite3"
)


<p class="indent">


在main函式加入:

// todo.go
func main() {

    db := initDB("storage.db")
    migrate(db)
<p class="indent">


定義建立資料庫和遷移資料表,initDB是開啟db檔案,如果不存在建立它。migrate是執行SQL建立Todo資料表。

// todo.go
func initDB(filepath string) *sql.DB {
    db, err := sql.Open("sqlite3", filepath)

    // Here we check for any db errors then exit
    if err != nil {
        panic(err)
    }

    // If we don't get any errors but somehow still don't get a db connection
    // we exit as well
    if db == nil {
        panic("db nil")
    }
    return db
}

func migrate(db *sql.DB) {
    sql := `
    CREATE TABLE IF NOT EXISTS tasks(
        id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
        name VARCHAR NOT NULL
    );
    `

    _, err := db.Exec(sql)
    // Exit if something goes wrong with our SQL statement above
    if err != nil {
        panic(err)
    }
}
<p class="indent">


再次啟動:
$ go build todo.go
$ ./todo

應該看到資料庫檔案存在目錄,使用SQLite管理命令列進入可檢視到資料庫。

最後我們建立處理器Handler和Model,將路由接受和資料庫連線起來。

Handler部分:
匯入庫:

package main
import (
    "database/sql"
    "go-echo-vue/handlers"

    "github.com/labstack/echo"
    "github.com/labstack/echo/engine/standard"
    _ "github.com/mattn/go-sqlite3"
)
<p class="indent">


用將建立的新handler設定路由:

// todo.go
    e := echo.New()

    e.File("/", "public/index.html")
    e.GET("/tasks", handlers.GetTasks(db))
    e.PUT("/tasks", handlers.PutTask(db))
    e.DELETE("/tasks/:id", handlers.DeleteTask(db))

    e.Run(standard.New(":8000"))
}
<p class="indent">


Handler程式碼:

// handlers/tasks.go
package handlers

import (
    "database/sql"
    "net/http"
    "strconv"

    "github.com/labstack/echo"
)

// handlers/tasks.go

// GetTasks endpoint
func GetTasks(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        return c.JSON(http.StatusOK, "tasks")
    }
}

// PutTask endpoint
func PutTask(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        return c.JSON(http.StatusCreated, H{
            "created": 123,
    }
}

// DeleteTask endpoint
func DeleteTask(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        id, _ := strconv.Atoi(c.Param("id"))
        return c.JSON(http.StatusOK, H{
            "deleted": id,
        })
    }
}

<p class="indent">


Model部分程式碼,包括業務模型和資料庫CRUD:

// models/tasks.go
package models

import (
    "database/sql"

    _ "github.com/mattn/go-sqlite3"
)
// models/tasks.go

// Task is a struct containing Task data
type Task struct {
    ID   int    `json:"id"`
    Name string `json:"name"`
}

// TaskCollection is collection of Tasks
type TaskCollection struct {
    Tasks []Task `json:"items"`
}

func GetTasks(db *sql.DB) TaskCollection {
    sql := "SELECT * FROM tasks"
    rows, err := db.Query(sql)
    // Exit if the SQL doesn't work for some reason
    if err != nil {
        panic(err)
    }
    // make sure to cleanup when the program exits
    defer rows.Close()

    result := TaskCollection{}
    for rows.Next() {
        task := Task{}
        err2 := rows.Scan(&task.ID, &task.Name)
        // Exit if we get an error
        if err2 != nil {
            panic(err2)
        }
        result.Tasks = append(result.Tasks, task)
    }
    return result
}

func PutTask(db *sql.DB, name string) (int64, error) {
    sql := "INSERT INTO tasks(name) VALUES(?)"

    // Create a prepared SQL statement
    stmt, err := db.Prepare(sql)
    // Exit if we get an error
    if err != nil {
        panic(err)
    }
    // Make sure to cleanup after the program exits
    defer stmt.Close()

    // Replace the '?' in our prepared statement with 'name'
    result, err2 := stmt.Exec(name)
    // Exit if we get an error
    if err2 != nil {
        panic(err2)
    }

    return result.LastInsertId()
}

func DeleteTask(db *sql.DB, id int) (int64, error) {
    sql := "DELETE FROM tasks WHERE id = ?"

    // Create a prepared SQL statement
    stmt, err := db.Prepare(sql)
    // Exit if we get an error
    if err != nil {
        panic(err)
    }

    // Replace the '?' in our prepared statement with 'id'
    result, err2 := stmt.Exec(id)
    // Exit if we get an error
    if err2 != nil {
        panic(err2)
    }

    return result.RowsAffected()
}

<p class="indent">


將Model插入Handler程式碼:

// handlers/tasks.go
package handlers

import (
    "database/sql"
    "net/http"
    "strconv"

    "go-echo-vue/models"

    "github.com/labstack/echo"
)

// handlers/tasks.go

// GetTasks endpoint
func GetTasks(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        // Fetch tasks using our new model
        return c.JSON(http.StatusOK, models.GetTasks(db))
    }
}

// PutTask endpoint
func PutTask(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        // Instantiate a new task
        var task models.Task
        // Map imcoming JSON body to the new Task
        c.Bind(&task)
        // Add a task using our new model
        id, err := models.PutTask(db, task.Name)
        // Return a JSON response if successful
        if err == nil {
            return c.JSON(http.StatusCreated, H{
                "created": id,
            })
        // Handle any errors
        } else {
            return err
        }
    }
}

// DeleteTask endpoint
func DeleteTask(db *sql.DB) echo.HandlerFunc {
    return func(c echo.Context) error {
        id, _ := strconv.Atoi(c.Param("id"))
        // Use our new model to delete a task
        _, err := models.DeleteTask(db, id)
        // Return a JSON response on success
        if err == nil {
            return c.JSON(http.StatusOK, H{
                "deleted": id,
            })
        // Handle errors
        } else {
            return err
        }
    }
}
<p class="indent">


以上都是Go後端程式碼。待續:

相關文章