Glang+Vue+Mysql+Nginx实现前后端分离实例(二)

Glang+Vue+Mysql+Nginx实现前后端分离实例(二)

注意:本实例基于上一个实验:

Glang+Vue+Nginx实现前后端分离实例(一)

实验准备

实验名称:基于Golang+Vue+Nginx搭建图书管理系统
实验环境:两台Centos7虚拟机
  1、后端:node1,ip:192.168.141.53
  2、前端:node2,ip:192.168.141.69

实验步骤

1、在上一个实验(Glang+Vue+Nginx实现前后端分离实例(一))中,在node2中部署了nginx实现了反向代理,在node1中安装设置好了go及其环境变量,两台虚拟机均放行了相应端口。本次实验将在上一次实验的node1、node2上继续进行。

2、node1安装mysql数据库,并且进行初始化,然后创建我们所需要用到的数据库和数据表

[root@node1 ~]# yum -y install mariadb mariadb-server
[root@node1 ~]# systemctl restart mariadb.service
[root@node1 ~]# systemctl enable mariadb.service
[root@node1 ~]# firewall-cmd --add-port=3306/tcp --permanent 
success
[root@node1 ~]# firewall-cmd --reload
success
[root@node1 ~]# 
[root@node1 ~]# mysql_secure_installation
#初始化数据库,设置root密码为:123456

[root@node1 ~]# mysql -u root -p
Enter password: #123456
Welcome to the MariaDB monitor.  Commands end with ; or \g.
Your MariaDB connection id is 12
Server version: 5.5.64-MariaDB MariaDB Server

Copyright (c) 2000, 2018, Oracle, MariaDB Corporation Ab and others.

Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.

MariaDB [(none)]> CREATE DATABASE sql_books;
Query OK, 1 row affected (0.00 sec)
MariaDB [(none)]> grant all privileges on *.* to 'root'@'%' identified by '123456' with grant option; 
MariaDB [(none)]>
MariaDB [(none)]> use sql_books;
Database changed
MariaDB [sql_books]> CREATE TABLE `books` (
    ->     `id` BIGINT(20) NOT NULL AUTO_INCREMENT,
    ->     `isbn` VARCHAR(40) DEFAULT '',
    ->     `title` VARCHAR(40) DEFAULT '',
    ->     `author_firstname` VARCHAR(20) DEFAULT '',
    ->     `author_lastname` VARCHAR(20) DEFAULT '',
    ->     PRIMARY KEY(`id`)
    -> )ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
Query OK, 0 rows affected (0.01 sec)

MariaDB [sql_books]> exit
Bye
[root@node1 ~]# 

3、node1重新编辑运行后端rest.go文件

[root@node1 ~]# cd /home/gopath/
[root@node1 gopath]# yum install git -y
[root@node1 gopath]# go get github.com/go-sql-driver/mysql
[root@node1 gopath]# vim rest.go
[root@node1 gopath]# cat rest.go 
package main

import (
	"database/sql"
	"encoding/json"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
	"github.com/gorilla/mux"
	"io/ioutil"
	"log"
	"math/rand"
	"net/http"
	"strconv"
	"time"
)

//Book Struct
type  Book struct{
	ID int `json:"id"`
	Isbn string `json:"isbn"`
	Title string `json:"title"`
	Author Author `json:"author"`
}

//Author Struct
type Author struct {
	Firstname string `json:"firstname"`
	Lastname string `json:"lastname"`
}

// 定义一个全局对象db
var db *sql.DB

// 定义一个初始化数据库的函数
func initDB() (err error) {
	// DSN:Data Source Name
	dsn := "root:123456@tcp(192.168.141.53:3306)/sql_books"
	// 不会校验账号密码是否正确
	// 注意!!!这里不要使用:=,我们是给全局变量赋值,然后在main函数中使用全局变量db
	db, err = sql.Open("mysql", dsn)
	if err != nil {
		return err
	}
	// 尝试与数据库建立连接(校验dsn是否正确)
	err = db.Ping()
	if err != nil {
		return err
	}
	return nil
}

// 预处理查询单行示例
func prepareSingleQueryDemo(id int) (book Book){
	sqlStr := "select id, isbn, title,author_firstname,author_lastname from books where id = ?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	row, err:=stmt.Query(id)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	defer row.Close()
	var b Book
	for row.Next() {
		err := row.Scan(&b.ID, &b.Isbn, &b.Title,&b.Author.Firstname,&b.Author.Lastname)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return Book{}
		}
		fmt.Printf("ID:%s Isbn:%s Title:%s Author:%s-%s\n", b.ID, b.Isbn, b.Title,b.Author.Firstname,b.Author.Lastname)
	}
	fmt.Println(b)
	return b
}

// 预处理查询多行示例
func prepareMultiQueryDemo(id int) (bs []Book){
	sqlStr := "select id, isbn, title,author_firstname,author_lastname from books where id > ?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	rows, err := stmt.Query(id)
	if err != nil {
		fmt.Printf("query failed, err:%v\n", err)
		return
	}
	defer rows.Close()
	// 循环读取结果集中的数据
	for rows.Next() {
		var b Book
		err := rows.Scan(&b.ID, &b.Isbn, &b.Title,&b.Author.Firstname,&b.Author.Lastname)
		if err != nil {
			fmt.Printf("scan failed, err:%v\n", err)
			return []Book{}
		}
		fmt.Printf("ID:%d Isbn:%s Title:%s Author:%s-%s\n", b.ID, b.Isbn, b.Title,b.Author.Firstname,b.Author.Lastname)
		bs= append(bs, b)
	}
	return bs
}

// 预处理插入示例
func prepareInsertDemo(book *Book) {
	sqlStr := "insert into books(id,isbn,title,author_firstname,author_lastname) values (?,?,?,?,?)"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(book.ID, book.Isbn,book.Title,book.Author.Firstname,book.Author.Lastname)
	if err != nil {
		fmt.Printf("insert failed, err:%v\n", err)
		return
	}
	fmt.Println("insert success.")
}

// 预处理删除示例
func prepareDeleteDemo(id int) {
	sqlStr := "delete from books where id = ?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(id)
	if err != nil {
		fmt.Printf("delete failed, err:%v\n", err)
		return
	}
	fmt.Println("delete success.")
}

// 预处理更新示例
func prepareUpdateDemo(book *Book) {
	sqlStr := "update books set isbn=?,title=?,author_firstname=?,author_lastname=? where id = ?"
	stmt, err := db.Prepare(sqlStr)
	if err != nil {
		fmt.Printf("prepare failed, err:%v\n", err)
		return
	}
	defer stmt.Close()
	_, err = stmt.Exec(book.Isbn,book.Title,book.Author.Firstname,book.Author.Lastname,book.ID)
	if err != nil {
		fmt.Printf("update failed, err:%v\n", err)
		return
	}
	fmt.Println("update success.")
}


//Get all Books
func getBooks(w http.ResponseWriter,r *http.Request){
	w.Header().Set("Content-Type","application/json")
	bs:=prepareMultiQueryDemo(0) //默认查询数据库内的所有book
	json.NewEncoder(w).Encode(bs)
}

//Get single Books
func getBook(w http.ResponseWriter,r *http.Request){
	w.Header().Set("Content-Type","application/json")
	params:=mux.Vars(r) //参数
	//for _,item:=range books{
	//	if item.ID==params["id"]{
	//		json.NewEncoder(w).Encode(item)
	//		return
	//	}
	//}
	//json.NewEncoder(w).Encode(&Book{})
	int_id, err := strconv.Atoi(params["id"])
	if err != nil {
		panic(err)
	}
	item:=prepareSingleQueryDemo(int_id)
	json.NewEncoder(w).Encode(item)
	return
}

//Create a new Book
func createBook(w http.ResponseWriter,r *http.Request){
	w.Header().Set("Content-Type","application/json")
	var book Book
	_ = json.NewDecoder(r.Body).Decode(&book)
	rand := rand.New(rand.NewSource(time.Now().UnixNano()))
	book.ID=rand.Intn(1000000000)
	json.NewEncoder(w).Encode(book)
	prepareInsertDemo(&book)
}

func updateBook(w http.ResponseWriter,r *http.Request){
	w.Header().Set("Content-Type","application/json")
	params:=mux.Vars(r) //参数
	bs:=prepareMultiQueryDemo(0)
	int_id, err := strconv.Atoi(params["id"])
	if err != nil {
		panic(err)
	}
	for _,item:=range bs{
		if item.ID==int_id{
			var book Book
			_ = json.NewDecoder(r.Body).Decode(&book)
			book.ID=int_id
			book.Isbn=params["isbn"]
			book.Title=params["title"]
			book.Author.Firstname=params["author_firstname"]
			book.Author.Lastname=params["author_lastname"]
			prepareUpdateDemo(&book)
			json.NewEncoder(w).Encode(book)
		}
	}
	json.NewEncoder(w).Encode(&Book{})
}

func deleteBook(w http.ResponseWriter,r *http.Request){
	w.Header().Set("Content-Type","application/json")
	params:=mux.Vars(r) //参数
	bs:=prepareMultiQueryDemo(0)
	int_id, err := strconv.Atoi(params["id"])
	if err != nil {
		panic(err)
	}
	for _,item:=range bs{
		if item.ID==int_id{
			prepareDeleteDemo(int_id)
			break
		}
	}
	json.NewEncoder(w).Encode(bs)
}

func index(w http.ResponseWriter,r *http.Request){
	//_, _ = fmt.Fprint(w,"hello")
	b,_:=ioutil.ReadFile("index.html")
	_, _ = fmt.Fprint(w,string(b))
}

func img(w http.ResponseWriter,r *http.Request){
	imgpath := "images/2.gif"
	http.ServeFile(w, r, imgpath)
}

func ico(w http.ResponseWriter,r *http.Request){
	imgpath := "images/favicon.ico"
	http.ServeFile(w, r, imgpath)
}

func main(){
	// 调用输出化数据库的函数
	err := initDB()
	if err != nil {
		fmt.Printf("init db failed,err:%v\n", err)
		return
	}

	//Init Router
	r:=mux.NewRouter()

	////mock Data 模拟数据
	//books=append(books,Book{
	//	ID:"1",
	//	Isbn:"448743",
	//	Title:"Book one",
	//	Author:&Author{
	//		Firstname:"John",
	//		Lastname:"Doe",
	//	},
	//})
	//books=append(books,Book{
	//	ID:"2",
	//	Isbn:"448232",
	//	Title:"Book two",
	//	Author:&Author{
	//		Firstname:"Tom",
	//		Lastname:"Das",
	//	},
	//})
	//books=append(books,Book{
	//	ID:"3",
	//	Isbn:"412343",
	//	Title:"Book three",
	//	Author:&Author{
	//		Firstname:"Jerry",
	//		Lastname:"Dac",
	//	},
	//})


	//Route Handlers
	r.HandleFunc("/",index)
	r.HandleFunc("/images/2.gif",img)
	r.HandleFunc("/images/favicon.ico",ico)
	r.HandleFunc("/api/books",getBooks).Methods("GET")
	r.HandleFunc("/api/books/{id}",getBook).Methods("GET")
	r.HandleFunc("/api/books",createBook).Methods("POST")
	r.HandleFunc("/api/books/{id}",updateBook).Methods("PUT")
	r.HandleFunc("/api/books/{id}",deleteBook).Methods("DELETE")

	log.Fatal(http.ListenAndServe(":9090",r))
}
[root@node1 gopath]#  
[root@node1 gopath]# go build rest.go 
[root@node1 gopath]# chmod +x rest
[root@node1 gopath]# ./rest

4、node2重新编辑index.html,重启nginx服务

[root@node2 ~]# cd /usr/share/nginx/html/
[root@node2 html]# rm -f index.html 
[root@node2 html]# vim index.html
[root@node2 html]# cat index.html 
<!DOCTYPE html>
<html lang="en">

<head>
    <title></title>
    <link rel="shortcut icon" href="images/favicon.ico">
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <script src="https://cdn.staticfile.org/vue/2.4.2/vue.min.js"></script>
    <script src="https://cdn.staticfile.org/axios/0.18.0/axios.min.js"></script>
    <style>

        #add{
            width:800px;
            padding:20px;
            margin:10px auto;
        }
        .tb{
            border-collapse:collapse;
            width: 100%;
        }
        .tb th{
            background-color: #0094ff;
            color:white;
        }

        .tb td,.tb th{
            padding:5px;
            border:1px solid black;
            text-align: center;
        }

        .add{
            padding: 5px;
            border:1px solid black;
            margin-bottom: 10px;
        }
    </style>
</head>

<body>
<div id="app">
    <div class="add">
        isbn:<input type="text" v-model="newIsbn" ref="idRef">&nbsp;&nbsp;&nbsp;&nbsp;
        title:<input type="text" v-model="newTitle" ref="titleRef">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
        author's firstname:    
        author's lastname:<input type="text" v-model="lastName" ref="firstnameRef">&nbsp;&nbsp;&nbsp;&nbsp;
        <input type="button" value="添加" @click="addData">
    </div>
    <br>
    <div class="add">
        搜索书本:<input type="text" placeholder="请输入书本ID:" v-model="searchId" @keydown.enter="search">
        <table class="tb">
            <tr>
                <th>编号</th>
                <th>ISBN</th>
                <th>标题</th>
                <th>作者</th>
            </tr>
            <tr>
                <td><b>{{searchResult.id}}</b></td>
                <td><b>{{searchResult.isbn}}</b></td>
                <td><b>{{searchResult.title}}</b></td>
                <td><b v-for="(v, k) in searchResult.author">{{ v }} </b></td>
            </tr>
            <tr>
                <td colspan="4"><br></td>
            </tr>
        </table>
    </div>
    <br>
    <div>
        <table class="tb">
            <tr>
                <th>编号</th>
                <th>ISBN</th>
                <th>标题</th>
                <th>作者</th>
                <th>操作</th>
            </tr>
            <tr v-for="item in list">
                <td>{{item.id}}</td>
                <td>{{item.isbn}}</td>
                <td>{{item.title}}</td>
                <td>{{item.author.firstname}}-{{item.author.lastname}}</td>
                <td>
                    <button @click="deleteData(item.id)">删除</button>
                </td>
            </tr>


        </table>
    </div>
</div>
<div align="center">
    <br>
    <br>
    <img border="0" src='images/2.gif' alt="Pulpit rock">
</div>
</body>
<script>
    let vm = new Vue({
        el: '#app',
        data: {
            newIsbn: '',
            newTitle: '',
            firstName:'',
            lastName:'',
            list: [],
            searchResult:{},
            searchId: ''
        },
        methods: {
            // 添加数据
            addData() {
                let url = 'http://192.168.141.69/api/books'
                axios.post(url, {
                    "isbn": this.newIsbn,
                    "title": this.newTitle,
                    "author": {
                        "firstname": this.firstName,
                        "lastname": this.lastName
                    }
                })
                    .then(res => {
                        // console.log(res);
                        if(res.status === 200) {
                            // 重新刷新一下列表数据
                            this.getList()
                        }
                    })
            },
            // 获取列表数据方法
            getList() {
                axios
                    .get('http://192.168.141.69/api/books')
                    .then(response => {
                        Vue.set(this.list = response.data);
                        console.log(this.list);
                    })
                    .catch(err => {
                        console.log(err);
                    })
            },
            // 搜索方法
            search() {
                let url = 'http://192.168.141.69/api/books/'
                axios.get(url+this.searchId)
                    .then(response => {
                        Vue.set(this.searchResult = response.data);
                        console.log(this.searchResult);
                    })
                    .catch(err => {
                        console.log(err);
                    })
            },
            // 删除方法
            deleteData(id) {
                let url = `http://192.168.141.69/api/books/${id}`
                axios.delete(url)
                    .then(res => {
                        this.getList()
                    })
            }
        },
        mounted () {
            this.getList()
        }
    })
</script>

</html>
[root@node2 html]# systemctl restart nginx
[root@node2 html]# 

5、浏览前端网页,如下图
Glang+Vue+Mysql+Nginx实现前后端分离实例(二)_第1张图片

6、功能调试
1)添加书本
Glang+Vue+Mysql+Nginx实现前后端分离实例(二)_第2张图片
2)确认书本信息已经保存到数据库
Glang+Vue+Mysql+Nginx实现前后端分离实例(二)_第3张图片
3)查询功能
Glang+Vue+Mysql+Nginx实现前后端分离实例(二)_第4张图片
4)删除功能
Glang+Vue+Mysql+Nginx实现前后端分离实例(二)_第5张图片
5)update功能
改数据可以先delete再insert,这里就不单独做改数据的功能了。

7、注意事项
有时候go build xxx.go会出现如下报错:

rest.go:6:2: cannot find package "github.com/gorilla/mux" in any of:
	/usr/local/go/src/github.com/gorilla/mux (from $GOROOT)
	/root/go/src/github.com/gorilla/mux (from $GOPATH)

这是因为没有事先go get相应的引用包,而通常go get下来的包都放在/usr/local/go/src/或者/root/go/src/路径下。部分包还需要安装git,可以事先使用yum install git -y

你可能感兴趣的:(Golang,golang,vue,nginx,mysql)