Golang小项目(2)
Golang 小项目(2)
前言
本项目适合 Golang 初学者,通过简单的项目实践来加深对 Golang 的基本语法和 Web 开发的理解。
正文
项目结构
.
└─src
├─go-basic-server
├─go-code
├─go-keword-scraper
├─go-movies-curd
│ go.mod
│ go.sum
│ main.go
│
├─go-rest
├─go-server
└─go-todo
项目结构图
为降低难度,本项目未使用
DATEBASE
,仅使用本地 json 文件存储数据。
-
/movie
:GET
方法获取所有电影信息,调用 getMovies() 函数获取所有数据 -
/movies/id
:GET
方法获取指定电影信息, 调用 getMovie() 函数获取单条数据 -
/movies
:POST
方法新增电影信息, 调用 createMovie() 函数新增单条数据 -
/movies/id
:PUT
方法更新指定电影信息, 调用 updateMovie() 函数更新单条数据 -
/movies/id
:DELETE
方法删除指定电影信息, 调用 deleteMovie() 函数删除单条数据
最后运用
postman
测试接口,验证项目功能是否正常。
项目初始化
-
创建项目文件夹
go-curd02
-
在项目文件夹下创建
src
文件夹,并在src
文件夹下创建go-basic-server
、go-code
、go-keword-scraper
、go-movies-curd
、go-rest
、go-server
、go-todo
共六个文件夹 -
在
go-movies-curd
文件夹下使用 cmd 命令下载github.com/gorilla/mux
包,并在go.mod
文件中添加依赖
// 依次使用以下命令初始化 mod 文件并下载依赖包
go mod init go-movies-curd
go get github.com/gorilla/mux@latest
// cmd 命令打开 VSCode
code .
- 在
go-movies-curd
文件夹下创建main.go
文件
项目实现
- 导入依赖包
package main
import (
"fmt" // 导入 fmt 包,用于格式化输入输出
"log" // 导入 log 包,用于日志记录和错误处理 -> 用于记录日志
"encoding/json" // 导入 encoding/json 包,用于处理 JSON 编码和解码 -> 用于处理JSON数据(postman测试)
"math/rand" // 导入 math/rand 包,用于生成随机数 -> 用于生成id
"net/http" // 导入 net/http 包,用于创建 HTTP 服务器和处理 HTTP 请求 -> 用于创建HTTP服务器
"strconv" // 导入 strconv 包,用于字符串和其他基本数据类型的转换 -> 便于将id转化为string类型
"github.com/gorilla/mux" // 导入 Gorilla Mux 包,用于处理 HTTP 路由 -> 用于处理API接口
)
- 定义数据结构体
定义电影数据结构体,用于存储电影信息
type Movie struct {
ID string `json:"id"`
Isbn string `json:"isbn"`
Title string `json:"title"`
Director *Director `json:"director"`
}
定义导演数据结构体,用于存储导演信息
type Director struct {
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
}
今日小知识: 结构体标签(struct tags)
- 定义:结构体标签是一个字符串,用反引号括起来,通常位于字段定义的后面。
- 主要用途:
JSON
编码和解码、数据库字段映射、表单解析和XML
编码和解码
示例:
//结构体标签最常见的用途是与 JSON 编码和解码相关。通过指定 JSON 标签,可以控制 JSON 数据中的字段名称和行为。 type Person struct { Name string `json:"name"` Age int `json:"age"` Email string `json:"email"` } // 当你使用 encoding/json 包对这个结构体进行编码时,json:"name" 标签指定了在 JSON 数据中应该使用 "name" 作为字段名。这使得 JSON 数据与 Go 结构体字段之间能够正确映射。 import ( "encoding/json" "fmt" ) func main() { p := Person{Name: "Alice", Age: 30, Email: "alice@example.com"} jsonData, _ := json.Marshal(p) fmt.Println(string(jsonData)) // 输出: {"name":"Alice","age":30,>>"email":"alice@example.com"} }
// 结构体标签也可以用于数据库操作,通过标签指定字段名与数据库表中的列名之间的映射。 // 这里,gorm:"column:user_name" 标签指定了结构体字段 Name 对应数据库表中的 user_name 列。 type User struct { ID int `gorm:"primary_key"` Name string `gorm:"column:user_name"` Email string `gorm:"column:user_email"` }
// 在 Web 开发中,结构体标签可以用于表单解析,指定表单字段的名称与结构体字段之间的映射关系。例如,使用 gin 框架处理表单数据: // 在这个例子中,form:"username" 标签表示表单字段 username 将被映射到结构体字段 Username。 type LoginForm struct { Username string `form:"username"` Password string `form:"password"` }
// 除了 JSON,结构体标签也可以用于 XML 编码和解码。例如,使用 encoding/xml 包: // 这里,xml:"title" 标签指定了 XML 数据中字段名为 title,这在 XML 编码和解码时起作用。 type Book struct { Title string `xml:"title"` Author string `xml:"author"` }
- 定义全局变量
var movies []Movie
- 定义函数
根据项目结构图创建相应函数
func main(){
// 创建路由实例
r := mux.NewRouter()
// 创建用于测试的电影数据
movies = append(movies, Movie{ID: "1",Isbn: "438227", Title: "Movie One", Director: &Director{Firstname: "John", Lastname: "Doe"}})
movies = append(movies, Movie{ID: "2",Isbn: "45455", Title: "Movie Two", Director: &Director{Firstname: "Steve", Lastname: "Smith"}})
// 定义路由以及视图函数
r.HandleFunc("/movies", getMovies).Methods("GET")
r.HandleFunc("/movies/{id}", getMovie).Methods("GET")
r.HandleFunc("/movies", createMovie).Methods("POST")
r.HandleFunc("/movies/{id}", updateMovie).Methods("PUT")
r.HandleFunc("/movies/{id}", deleteMovie).Methods("DELETE")
fmt.Printf("Starting server at port 8000\n")
// 监听 8000 端口和路由器 r, 如果启动服务器过程中出现任何错误(例如端口已被占用或其他问题),log.Fatal 会记录错误信息并退出程序。
log.Fatal(http.ListenAndServe(":8000",r))
}
*
和&
傻傻分不清?
在 Go 语言中,&
是一个运算符,它用于获取(或查找)变量的地址。你可以把这看作是“给变量取一个标记”的过程。实际上,& 就像在说“这个变量的房子在哪里”。
举个栗子:
var x int = 10 var p *int p = &x
上面这段代码中,我们声明了一个名为
x
的整型变量,并且给它取了一个值为10
。然后我们声明了一个名为p
的指针类型的整型变量(指针变量),你会注意到它的类型*int
里面有星号。
这个星号*
表示这个变量p
是一个指针,它可以持有地址。然后我们使用&x
,这是什么意思呢?就是这个整型变量x
的 “地址标记”,我们把这个“地址标记”赋给了p
。
总结:&
就是给变量取一个标记,也就是获取变量的地址;*
表示“我想要地址所指向位置的值”,用于解引用指针。
再浅显一点就是,*
用于定义指针变量: 就像定义一个“指针容器”,它准备好来存放指向某种类型的地址。例如,_int 表示一个“整数指针容器”,它可以存放一个整数的地址。
&
用于确定地址: 当你有一个具体的变量,并且想知道它在内存中的地址时,你使用&
。这个地址将被存放到你之前定义好的“指针容器”里。
速记:_
从地址取值,&
找地址。
- 定义路由函数
// 定义用于获取所有电影数据的函数
func getMovies(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(movies)
}
// 定义用于删除指定电影数据的函数
func deleteMovie(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
// 从 HTTP 请求 r 中提取路径参数,并将它们以 map[string]string 的形式返回
params := mux.Vars(r)
// index 是当前元素的索引(即位置),它是一个整数值; item 是当前索引位置上的元素本身
for index, item := range movies {
if item.ID == params["id"] {
// append(movies[:index], movies[index+1:]...) 实际上创建了一个新的切片,其中包含了删除指定元素后的所有元素。
movies = append(movies[:index], movies[index+1:]...)
// break 语句用于退出 for 循环
break
}
}
json.NewEncoder(w).Encode(movies)
}
func getMovie(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type","application/json")
params := mux.Vars(r)
for _, item := range movies {
if item.ID == params["id"] {
json.NewEncoder(w).Encode(item)
return
}
}
}
func createMovie(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
var movie Movie
// Decode(&movie):传入 movie 的指针,以便解码器可以直接修改 movie 变量的内容。
// _ 表示我们忽略 Decode 方法的返回值。如果解码过程发生错误,通常会记录错误信息而不是简单地忽略。
_ = json.NewDecoder(r.Body).Decode(&movie)
// rand.Intn(100000000):生成一个随机整数,范围是 0 到 99999999(不包括 100000000)。
movie.ID = strconv.Itoa(rand.Intn(100000000))
// 将 movie 添加到 movies 切片的末尾。append 函数会创建一个新的切片(如果原切片空间不足),并将原切片和新元素合并到一起。
movies = append(movies, movie)
json.NewEncoder(w).Encode(movie)
}
func updateMovie(w http.ResponseWriter, r *http.Request) {
// 设定响应头 Content-Type 为 application/json
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(r)
for index, item := range movies {
if item.ID == params["id"] {
// 删除原有电影
movies = append(movies[:index], movies[index+1:]...)
// 创建新电影
var movie Movie
// 创建一个 JSON 解码器,并从 HTTP 请求的主体中解码 JSON 数据到 `movie` 变量。
_ = json.NewDecoder(r.Body).Decode(&movie)
movie.ID = params["id"]
movies = append(movies, movie)
json.NewEncoder(w).Encode(movies)
return
}
}
}
- 运行项目
go build main.go
go run main.go
- 测试接口
进入Postman测试接口,验证项目功能是否正常。
GET ALL
接口地址:http://localhost:8000/movies
GET BY ID
接口地址:http://localhost:8000/movies/1
CREATE
接口地址:http://localhost:8000/movies
UPDATE
接口地址:http://localhost:8000/movies/1
DELETE
接口地址:http://localhost:8000/movies/1
- 感谢你赐予我前进的力量