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">
title:<input type="text" v-model="newTitle" ref="titleRef">
author's firstname:
author's lastname:<input type="text" v-model="lastName" ref="firstnameRef">
<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]#
6、功能调试
1)添加书本
2)确认书本信息已经保存到数据库
3)查询功能
4)删除功能
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
。