使用 Gorilla Mux 和 CockroachDB 编写可维护 REST

想了解更多关于开源的使用内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com
一、和C护前言
本文将使用功能强大的编写 Gorilla Mux、GORM 和 CockroachDB 编写可维护 RESTful API。使用利用到的和C护 Go 语言相关技术有:
Gorilla/Mux:功能强大的 URL 路由器和调度组件。CockroachDB:开源,编写云原生分布式 SQL 数据库系统。使用GORM:神奇的和C护 ORM 库。运行环境:
Ubuntu 18.04.6 LTS。编写开发工具:Visual Studio Code。使用测试工具:APIfox:Apifox = Postman + Swagger + Mock + JMeter。和C护二、编写CockroachDB 介绍

CockrocreDB 是使用一个云原生分布式 SQL 数据库,旨在构建,和C护扩展和管理现代数据密集型应用程序。编写
官方介绍翻译如下:
CockroachDB 是一个分布式关系型数据库,建立在事务性和强一致性键值存储之上,具有水平扩展、高可用、支持强一致性 ACID 事务隔离级别的特点,并提供统一的 SQL API 用于结构化、操作和查询数据。
简单来说,CockroachDB 充分利用了上一代数据库系统的优势、b2b信息网SQL 的强大能力以及关系数据模型和现代云原生法则,成为了一个与其他基于 SQL 的事务型数据库广泛兼容的数据库系统。
CockroachDB 对 Go 也有很好的支持,比如 pgx、pg、GORM 和 upper/db。
1、下载安装
针对最新版的 CockroachDB Linux 下载,点此处 可以看官方的教程写的很详细,这里本文将跟着照做一次。(选择其他系统,同理)。
下载适用于 Linux 的 CockroachDB 包和其支持库,并将二进制文件复制到您的 PATH 中,以便您可以从任何 shell 执行 cockroach 命令: 复制curl https://binaries.cockroachdb.com/cockroach-v22.1.3.linux-amd64.tgz | tar -xz && sudo cp -i cockroach-v22.1.3.linux-amd64/cockroach /usr/local/bin/1.如图所示:

所以,您可以将二进制文件复制到您的路径中,以便您可以从任何目录执行 Cockroach 命令:
复制sudo cp -R cockroach-v22.1.3.linux-amd64/* /usr/local/bin1. 确保刚刚安装的 cockroach 已经成功安装: 复制which cockroach1.

如果能看到上图,说明 CockroachDB 安装成功,恭喜~
2、使用本地集群
官方提供两种方式利用 GORM 使用 CockroachDB:一种是 CockroachDB Serverless(测试阶段),另一种是使用本地集群。

第一种方式需要注册一个 CockroachDB 云账号,然后按照官方提示创建链接。
本文将使用第二种方式:
运行cockroach start-single-node 命令: 复制cockroach start-single-node --advertise-addr localhost --insecure1.通过增加 --insecure 参数启动了一个不安全的单节点群集。
记录下 SQL Shell 中欢迎文本中以下连接信息: 复制CockroachDB node starting at 2022-07-14 13:21:35.179273246 +0000 UTC (took 1.6s)build: CCL v22.1.3 @ 2022/07/11 14:04:38 (go1.17.11)webui: http://localhost:8080sql: postgresql://root@localhost:26257/defaultdb?sslmode=disable
sql (JDBC): jdbc:postgresql://localhost:26257/defaultdb?sslmode=disable&user=root
RPC client flags: cockroach <client cmd> --host=localhost:26257 --insecurelogs: /home/yuzhou/cockroach-data/logs
temp dir: /home/yuzhou/cockroach-data/cockroach-temp2801178939
external I/O path: /home/yuzhou/cockroach-data/extern
store[0]: path=/home/yuzhou/cockroach-data
storage engine:pebble
clusterID: 43919fea-32cd-48f9-b585-713731d43ee9
status: restarted pre-existing node
nodeID: 11.2.3.4.5.6.7.8.9.10.11.12.13.14.其中,postgresql://root@localhost:26257/defaultdb?sslmode=disable ,通过 SQL 连接字符串连接我们的 CockroachDB 集群中。
PS:这条信息需要加入到后续的数据库连接 datebase.go 文件中,请注意。
三、项目介绍
1、项目功能
本文创建一个应用是一个简单的 RESTful API 服务器:线上书店,它将公开书籍的访问和操作,主要包括如下功能:
创建一本书籍获取书籍清单获取一本书籍信息更新已有书籍信息删除一本书籍2、API 接口规范
为了实现上述的功能,我们的应用需要提供给外界如下的 API 接口规范:
创建一本书籍:通过/book 响应有效的 POST 请求。获取书籍清单:通过/books 响应有效的香港云服务器 GET 请求。获取一本书籍:通过/book/{id} 响应对应的 GET 请求。更新一本书籍:通过/book/{id} 响应对应的 PUT 请求。删除一本书籍:通过/book/{id} 响应对应的 DELETE 请求。通过 {id} 能够有效确定某本书籍。
豆瓣图书链接中 https://book.douban.com/subject/26859123/ 的 26859123 就能对应到 《Go程序设计语言(英文版)》这本书。

3、项目结构
本文将创建一个非常简单的应用程序结构,这是本文使用的 mybook 应用的项目结构:
复制.
├── go.mod├── go.sum├── handlers.go├── main.go└── model ├── database.go └── model.go1.2.3.4.5.6.7.8.4、安装项目依赖
安装 go get -u github.com/gorilla/mux,如下。

安装 go get -u gorm.io/gorm。
安装 go get -u gorm.io/driver/postgres。

四、应用功能设计
首先创建一个 mybook 的目录(应用文件夹),然后使用 go mod init mybook。
接着在其中创建我们的 model 文件夹,接下来,在 model 文件夹下新建一个 model.go 文件。
1、model.go
我们需要设计出 book 模型,这里即将使用 GORM 进行创建,编写我们的 model.go 函数:
复制package modelimport(
"gorm.io/gorm")
type Book struct{
gorm.Model ID string `json:"id"` Name string `json:"name"` Author string `json:"author"` Description string `json:"description"` Price float64 `json:"price"` Category string `json:"category"`}1.2.3.4.5.6.7.8.9.10.11.12.13.14.如上所示,书籍的结构体中进行简单设计包含 id(ID)、书名(Name)、作者(Author)、书籍描述(Description)、价格(Price)和类别(Category)。
2、database.go
该程序将创建数据库连接,需要使用到 postgresql://root@localhost:26257/defaultdb?sslmode=disable 传入到 gorm.Open() 的连接参数中,代码如下:
复制package modelimport(
"log" "time" "gorm.io/driver/postgres" "gorm.io/gorm")
func SetupDB() (*gorm.DB, error) {
dsn := "postgresql://root@localhost:26257/defaultdb?sslmode=disable" db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
if err != nil{
log.Fatal("cant connect to database", err)
}
var now time.Time db.Raw("SELECT NOW()").Scan(&now)
log.Println(now)
if err = db.AutoMigrate(&Book{}); err != nil{
log.Println(err)
}
return db, err}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.3、handler.go
回到 mybook 文件夹下,创建 handler.go :
复制package mainimport(
"encoding/json" "net/http" "mybook/model" "github.com/gorilla/mux" "gorm.io/gorm")
type Server struct{
db *gorm.DB}
type UpdateBook struct{
Price float64 `json:"price"` Description string `json:"decription"` Category string `json:"category"`}
func NewServer(db *gorm.DB) *Server{
return &Server{db: db}
}
func (s *Server) RegisterRouter(router *mux.Router) {
router.HandleFunc("/books", s.getBooks)
router.HandleFunc("/book/{id}", s.getBook).Methods("GET")
router.HandleFunc("/book", s.createBook).Methods("POST")
router.HandleFunc("/book/{id}", s.updateBook).Methods("PUT")
router.HandleFunc("/book/{id}", s.deleteBook).Methods("DELETE")
}
func (s *Server) getBooks(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json;charset=UTF-8")
var books []model.Book if err := s.db.Find(&books).Error; err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(books)
}
func (s *Server) createBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
var book model.Book if err := json.NewDecoder(r.Body).Decode(&book); err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return}
newBook := model.Book{Price: book.Price, Description: book.Description, Category: book.Category}
if err := s.db.Create(newBook).Error; err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(newBook)
}
func (s *Server) getBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
var book model.Book vars := mux.Vars(r)
id := vars["id"]
if err := s.db.Where("id = ?", id).First(&book).Error; err != nil{
http.Error(w, err.Error(), http.StatusNotFound)
return}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(book)
}
func (s *Server) updateBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
var updateBook UpdateBook var book model.Book vars := mux.Vars(r)
id := vars["id"]
if err := json.NewDecoder(r.Body).Decode(&updateBook); err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return}
if err := s.db.Where("id = ?", id).First(&book).Error; err != nil{
http.Error(w, err.Error(), http.StatusNotFound)
return}
if err := s.db.Model(&book).Updates(&model.Book{
Price: updateBook.Price,
Description: updateBook.Description,
Category: updateBook.Category}).Error; err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(book)
}
func (s *Server) deleteBook(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json; charset=UTF-8")
var book model.Book vars := mux.Vars(r)
id := vars["id"]
if err := s.db.Where("id = ?", id).First(&book).Error; err != nil{
http.Error(w, err.Error(), http.StatusNotFound)
return}
if err := s.db.Delete(&book).Error; err != nil{
http.Error(w, err.Error(), http.StatusInternalServerError)
return}
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode("Book Deleted Successfully!")
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.39.40.41.42.43.44.45.46.47.48.49.50.51.52.53.54.55.56.57.58.59.60.61.62.63.64.65.66.67.68.69.70.71.72.73.74.75.76.77.78.79.80.81.82.83.84.85.86.87.88.89.90.91.92.93.94.95.96.97.98.99.100.101.102.103.104.105.4、main.go
在 main.go 中,我们初始化数据库,创建一个路由器实例,将变量 db 作为参数传递给我们的服务器初始化,然后将路由器实例作为参数传递给方法 registryRouter()。然后,我们使用侦听器来运行服务器。
复制package mainimport(
"log" "net/http" "github.com/gorilla/mux" "mybook/model")
func main() {
db, err := model.SetupDB()
if err != nil{
log.Println("Failed setting up database")
}
router := mux.NewRouter()
server := NewServer(db)
server.RegisterRouter(router)
log.Fatal(http.ListenAndServe(":8000", router))
}1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.当写完所有的程序后,运行 go run . 命令,启动成功如下:

此时,运行 cockcoach sql 进入数据库命令行,然后运行 show tables; 命令,可以看到在默认数据库 defaultdb 中已经生成一个 books 数据库表(与我们的模型 books 同名),如下所示:

我们执行 select * from books 查看我们生成的表信息:

可以看到已经生成了我们定义的字段 id、name、author、description、price、category 以外,GORM 还默认帮忙增加了 created_at (创建时间)、updated_at(更新时间)和 deleted_at(删除时间)三个字段。
五、API 测试
为了方便我们测试这个 book 应用的 API 功能正常,我们将利用 APIfox 工具来进行测试。先在数据库中新增一条记录,如下:
复制INSERT INTO books (id, name, author, description, price, category)VALUES(1, Go程序设计语言(英文版), 艾伦A.A.多诺万 (Alan A.A.Donovan) / 布莱恩W.柯尼汉 (Brian W.Kemighan), 被誉为 Go 语言圣经的书,非常值得一读, 79.00, Golang);1.2.
获取书籍清单:/books测试。
插入成功,我们访问后台 http://127.0.0.1:8000/books 路径,可以看到如下成功,说明获取书籍清单的 API 是成功的,恭喜。

接下来为了测试,下载 Linux 版本的 Apifox 帮助我们快速测试其他接口。
获取一本书籍:/book/1 测试。

其他接口测试类似。
总结
本文利用 Go 语言中非常实用的 Gorilla Mux 和 GORM 库、结合分布式 CockroachDB 数据库编写了一个简易的图书的 Restful API,最后通过 Apifox 测试工具验证了服务器 API 的正确。学习了 CockroachDB 数据的安装,了解到 Gorilla Mux 和 GORM 的使用方法。
显然本文还是有很多不足,比如并没有充分利用到 CockroachDB 数据库的分布式特性,而且没有为 API 编写测试代码,测试并不一定完善,这些都可以溜给读者一些优化思路。
想了解更多关于开源的内容,请访问:
51CTO 开源基础软件社区
https://ost.51cto.com。
相关文章
华为电脑桌面设置教程(通过多个桌面设置,让华为电脑更适合你的需求)
摘要:随着数字化办公的发展,华为电脑成为了越来越多人的首选,而个性化的桌面设置则可以提升工作效率和使用体验。本文将为您介绍如何通过多个桌面设置,让华为电脑更加适合您的需求。创建新...2025-11-04小白充电器(方便快捷、安全高效的小白充电器为您提供完美充电体验)
摘要:随着科技的不断进步,人们对于充电器的需求也越来越高。传统充电器往往在充电速度、安全性和便携性方面存在诸多问题。而如今,以小白充电器作为主角的新一代充电器应运而生,它通过创新设计和先...2025-11-04联想电脑系统从U盘启动教程(简明易懂的操作步骤带你轻松实现)
摘要:在某些情况下,我们可能需要从U盘启动电脑系统,这种方式可以帮助我们修复电脑故障、安装操作系统等。而联想电脑作为一款常见的品牌,今天我们就来分享一下联想电脑系统如何从U盘启动的详细操...2025-11-04微星GP72VR散热性能评测(GP72VR散热怎么样?深度解析微星游戏笔记本的散热设计与性能表现)
摘要:作为一款专为游戏而设计的笔记本电脑,微星GP72VR以其高性能和出色的游戏体验而备受玩家的喜爱。然而,高性能往往伴随着高功耗和散热难题,因此散热性能对于一台游戏笔记本来说尤为重要。...2025-11-04电脑恢复重置教程(一步步教你如何进行电脑恢复重置,让电脑焕然一新)
摘要:在使用电脑的过程中,我们常常会遇到系统崩溃、软件冲突等问题。为了解决这些问题,电脑恢复重置成为了一个不错的选择。本教程将详细介绍如何进行电脑恢复重置的步骤,让你的电脑焕然一新。...2025-11-04- 摘要:在现代社会中,手机已经成为我们生活中必不可少的一部分。为了保护手机的同时展示个性,选择一款高品质的手机壳是非常重要的。摩斯维手机壳以其时尚外观和全面的保护功能而备受消费者喜爱。...2025-11-04

最新评论