Go语言教程之边写边学:web应用:读取和写入JSON数据的Web应用程序

在此示例中,我们将通过用户界面表单读取-写入-更新JSON文件中的JSON数据,获取特定记录的数据并删除特定记录的数据。

net/http标准库包提供了多种创建HTTP服务器的方法,并附带了一个基本的路由器。此处使用的net/http包用于向服务器发出请求并响应请求。

 

项目目录结构:

 

 

示例代码:main.go

导入net/http包。包http提供HTTP客户端和服务器实现。在main函数中,使用HandleFunc方法创建路由 /。HandleFunc为给定模式注册处理程序函数。HandleFunc阻止我们在执行处理程序之前和之后使用中间件执行任务。ListenAndServe使用给定的地址和处理程序启动HTTP服务器。

package main

import (
	handlers "./handlers"
	"net/http"
)

func main() {
	http.HandleFunc("/addnewuser/", handlers.AddNewUserFunc)
	http.HandleFunc("/notsucceded", handlers.NotSucceded)

	http.HandleFunc("/deleted", handlers.DeletedFunc)
	http.HandleFunc("/deleteuser/deleted", handlers.DeleteUserFunc)
	http.HandleFunc("/deleteuser/", handlers.DeleteUserServe)
	http.HandleFunc("/deleteuser/notsuccededdelete", handlers.NotSuccededDelete)

	http.HandleFunc("/", handlers.IndexFunc)

	http.HandleFunc("/showuser/show", handlers.ShowUserFunc)
	http.HandleFunc("/showuser/", handlers.ShowUser)
	http.HandleFunc("/showuser/notsuccededshow/", handlers.NotSuccededShow)

	http.ListenAndServe(":8080", nil)
}

 

示例代码:list.json

[
 {
  "id": 1,
  "firstName": "Mariya",
  "lastName": "Ivanova",
  "balance": 300
 },
 {
  "id": 2,
  "firstName": "EKatina",
  "lastName": "Milevskaya",
  "balance": 5000
 },
 {
  "id": 3,
  "firstName": "Vasek",
  "lastName": "Zalupickiy",
  "balance": 2000
 }
]

 

示例代码:handlers\handlers.go

package handlers

import (
	model "../model"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"os"
	"regexp"
	"strconv"
)

//function to check correct user adding input (regular expression and non-empty field input)
func checkFormValue(w http.ResponseWriter, r *http.Request, forms ...string) (res bool, errStr string) {
	for _, form := range forms {
		m, _ := regexp.MatchString("^[a-zA-Z]+$", r.FormValue(form))
		if r.FormValue(form) == "" {
			return false, "All forms must be completed"
		}
		if m == false {
			return false, "Use only english letters if firstname,lastname forms"
		}

	}
	return true, ""
}

func ShowUser(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "templates/showUser.html")
}

func NotSuccededShow(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "templates/notSuccededShow.html")

}

//handler to show user with id input
func ShowUserFunc(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		t, _ := template.ParseFiles("templates/showUserPage.html")
		t.Execute(w, nil)

	} else {

		id, err := strconv.Atoi(r.FormValue("id"))
		checkError(err)
		var alUsrs model.AllUsers
		file, err := os.OpenFile("list.json", os.O_RDONLY, 0666)
		checkError(err)
		b, err := ioutil.ReadAll(file)
		checkError(err)
		json.Unmarshal(b, &alUsrs.Users)

		var allID []int
		for _, usr := range alUsrs.Users {
			allID = append(allID, usr.Id)
		}
		for _, usr := range alUsrs.Users {
			if model.IsValueInSlice(allID, id) != true {
				http.Redirect(w, r, "/showuser/notsuccededshow/", 302)
				return
			}
			if usr.Id != id {
				continue
			} else {
				t, err := template.ParseFiles("templates/showUserPage.html")
				checkError(err)
				t.Execute(w, usr)
			}

		}
	}
}

//function to handle page with successful deletion
func DeletedFunc(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "templates/deleted.html")
}

//serving file with error (add function:empty field input or uncorrect input)
func NotSucceded(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "templates/notSucceded.html")
}

//function,which serve html file,when deleting was not succesful(id input is not correct)
func NotSuccededDelete(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "templates/notSuccededDelete.html")
}

//function,which serve page with delete information input
func DeleteUserServe(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "templates/deleteUser.html")

}

//function to delete user
func DeleteUserFunc(w http.ResponseWriter, r *http.Request) {
	if r.Method == "GET" {
		t, _ := template.ParseFiles("templates/deleteUser.html")
		t.Execute(w, nil)
	} else {
		r.ParseForm()
		id, err := strconv.Atoi(r.FormValue("id"))
		checkError(err)

		//open file with users
		file, err := os.OpenFile("list.json", os.O_RDWR|os.O_APPEND, 0666)
		defer file.Close()

		//read file and unmarshall json to []users
		b, err := ioutil.ReadAll(file)
		var alUsrs model.AllUsers
		err = json.Unmarshal(b, &alUsrs.Users)
		checkError(err)

		var allID []int
		for _, usr := range alUsrs.Users {
			allID = append(allID, usr.Id)
		}
		for i, usr := range alUsrs.Users {
			if model.IsValueInSlice(allID, id) != true {
				http.Redirect(w, r, "/deleteuser/notsuccededdelete", 302)
				return
			}
			if usr.Id != id {
				continue
			} else {
				alUsrs.Users = append(alUsrs.Users[:i], alUsrs.Users[i+1:]...)
			}

		}
		newUserBytes, err := json.MarshalIndent(&alUsrs.Users, "", " ")
		checkError(err)
		ioutil.WriteFile("list.json", newUserBytes, 0666)
		http.Redirect(w, r, "/deleted", 301)
	}
}

func checkError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

//function to add user
func AddNewUserFunc(w http.ResponseWriter, r *http.Request) {

	//creating new instance and checking method
	newUser := &model.User{}
	if r.Method == "GET" {
		t, _ := template.ParseFiles("templates/addNewUser.html")
		t.Execute(w, nil)

	} else {
		resBool, errStr := checkFormValue(w, r, "firstname", "lastname")
		if resBool == false {
			t, err := template.ParseFiles("templates/notSucceded.html")
			checkError(err)
			t.Execute(w, errStr)

			return
		}
		newUser.FirstName = r.FormValue("firstname")
		newUser.LastName = r.FormValue("lastname")
		var err error
		newUser.Balance, err = strconv.ParseFloat(r.FormValue("balance"), 64)
		checkError(err)

		//open file
		file, err := os.OpenFile("list.json", os.O_RDWR, 0644)
		checkError(err)
		defer file.Close()

		//read file and unmarshall json file to slice of users
		b, err := ioutil.ReadAll(file)
		var alUsrs model.AllUsers
		err = json.Unmarshal(b, &alUsrs.Users)
		checkError(err)
		max := 0

		//generation of id(last id at the json file+1)
		for _, usr := range alUsrs.Users {
			if usr.Id > max {
				max = usr.Id
			}
		}
		id := max + 1
		newUser.Id = id

		//appending newUser to slice of all Users and rewrite json file
		alUsrs.Users = append(alUsrs.Users, newUser)
		newUserBytes, err := json.MarshalIndent(&alUsrs.Users, "", " ")
		checkError(err)
		ioutil.WriteFile("list.json", newUserBytes, 0666)
		http.Redirect(w, r, "/", 301)

	}

}

//Index page handler
func IndexFunc(w http.ResponseWriter, r *http.Request) {
	au := model.ShowAllUsers()
	t, err := template.ParseFiles("templates/indexPage.html")
	checkError(err)
	t.Execute(w, au)
}

 

示例代码:model/model.go

package model

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
)

func checkError(err error) {
	if err != nil {
		fmt.Println(err)
	}
}

func IsValueInSlice(slice []int, value int) (result bool) {
	for _, n := range slice {
		if n == value {
			return true
		}

	}
	return false

}

type User struct {
	Id        int     `json:"id"`
	FirstName string  `json:"firstName"`
	LastName  string  `json:"lastName"`
	Balance   float64 `json:"balance"`
}

type AllUsers struct {
	Users []*User
}

func ShowAllUsers() (au *AllUsers) {
	file, err := os.OpenFile("list.json", os.O_RDWR|os.O_APPEND, 0666)
	checkError(err)
	b, err := ioutil.ReadAll(file)
	var alUsrs AllUsers
	json.Unmarshal(b, &alUsrs.Users)
	checkError(err)
	return &alUsrs
}

 

示例代码:templates\indexPage.html

<html>
<head>
	<title>User info</title>
</head>
<body>

{{range .Users}}
<div><h3> ID:{{ .Id }} </h3></div>
<div> First Name:{{ .FirstName }}</div>
<div> Last Name:{{ .LastName }} </div>
<div> Balance:{{ .Balance }}</div>
{{end}}

<h1>Options</h1>
<form action="/addnewuser/">
    <button type="submit">Add new user</button>
</form>
<form action="/deleteuser/">
    <button type="submit">Delete user</button>
</form>
<form action="/showuser/">
    <button type="submit">Show user</button>
</form>

</body>
</html>

 

示例代码:templates\addNewUser.html

<html>
<head>
	<title>Adding new user</title>
</head>
<body>
<h2>New user's data</h2>
        <form method="POST" action="useraded">
            <label>Enter firstname</label><br>
            <input type="text" name="firstname" /><br><br>
			<label>Enter lastname</label><br>
            <input type="text" name="lastname" /><br><br>
            <label>Enter balance</label><br>
            <input type="number" name="balance" /><br><br>
            <input type="submit" value="Submit" />
        </form>
</body>
</html>

 

示例代码:templates\deleteUser.html

<html>
<head>
	<title>Delete user</title>
</head>
<body>
<h2>Please,write an id of user you want to delete</h2>
        <form method="POST" action="deleted">
            <label>Enter id</label><br>
            <input type="text" name="id" /><br><br>
            <input type="submit" value="Submit" />
        </form>
</body>
</html>

 

示例代码:templates\deleted.html

<html>
<head>
	<title>User info</title>
</head>
<body>
	<div>Done</div><br><br>
	<form action="/">
		<button type="submit">Back to main</button>
	</form>
</body>
</html>

 

示例代码:templates\notSuccededDelete.html

<html>
<head>
	<title>User info</title>
</head>
<body>
<div>There is no user with such ID</div><br><br>
<form action="/">
    <button type="submit">Back to main</button>
</form>
</body>
</html>

 

示例代码:templates\notSuccededShow.html

<html>
<head>
	<title>User info</title>
</head>
<body>
<div>Error:Cant find User with such ID,try again</div><br><br>
<form action="/">
    <button type="submit">Back to main</button>
</form>
</body>
</html>

 

示例代码:templates\showUser.html

<html>
<head>
	<title>Show user</title>
</head>
<body>
<h2>Please,write an id of user you want to show</h2>
	<form method="POST" action="show">
		<label>Enter id</label><br>
		<input type="text" name="id" /><br><br>
		<input type="submit" value="Submit" />
	</form>
</body>
</html>

 

示例代码:templates\showUserPage.html

<html>
<head>
	<title>User info</title>
</head>
<body>
	<div><h3> ID:{{.Id}} </h3></div>
	<div> First Name:{{ .FirstName }}</div>
	<div> Last Name:{{ .LastName }} </div>
	<div> Balance:{{ .Balance }}</div>
</body>
</html>

 

示例代码:templates\notSucceded.html

<html>
<head>
	<title>User info</title>
</head>
<body>
<div>Error:{{.}}</div><br><br>
<form action="/">
    <button type="submit">Back to main</button>
</form>
</body>
</html>

 

运行应用程序:

go run main.go

 

在浏览器访问http://localhost:8080即可看到如下页面