当前位置: 首页 > 科技观察

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

时间:2023-03-20 02:15:35 科技观察

使用GorillaMux和CockroachDB编写可维护的REST本文将使用强大的GorillaMux、GORM和CockroachDB编写可维护的RESTfulAPI。使用的Go语言相关技术有:Gorilla/Mux:一个强大的URL路由器和调度组件。CockroachDB:开源的云原生分布式SQL数据库系统。GORM:神奇的ORM库。运行环境:Ubuntu18.04.6LTS。开发工具:VisualStudioCode。测试工具:APIfox:Apifox=Postman+Swagger+Mock+JMeter。2.CockroachDB简介CockrocreDB是一个云原生分布式SQL数据库,旨在构建、扩展和管理现代数据密集型应用程序。官方介绍翻译如下:CockroachDB是一个建立在事务性和强一致性键值存储之上的分布式关系数据库。用于结构化、操作和查询数据的SQLAPI。简单来说,CockroachDB充分利用了上一代数据库系统的优势,SQL和关系数据模型的强大能力以及现代云原生原理,成为一个广泛兼容其他基于SQL的事务型数据库的数据库系统.CockroachDB对Go也有很好的支持,比如pgx、pg、GORM和upper/db。1、下载安装最新版CockroachDBLinux下载,点此查看官方写的详细教程,本文照做一次。(选择其他系统,同理)。下载适用于Linux的CockroachDB软件包及其支持库,并将二进制文件复制到您的PATH,以便您可以从任何shell执行cockroach命令:curlhttps://binaries.cockroachdb.com/cockroach-v22.1.3。linux-amd64.tgz|tar-xz&&sudocp-icockroach-v22.1.3.linux-amd64/cockroach/usr/local/bin/如图:CockroachDB使用定制版的GEOS库。默认情况下,CockroachDB在/usr/local/lib/cockroach或CockroachDB二进制文件当前目录的lib子目录中查找外部链接库。所以,你可以将二进制文件复制到你的路径中,这样你就可以从任何目录执行cockroach命令:sudocp-Rcockroach-v22.1.3.linux-amd64/*/usr/local/bin确保你刚刚安装了cockroach它有已安装成功:启动了哪个cockroach,用于启动临时本地内存集群,使用cockroachdemo命令:如果能看到上图,说明CockroachDB安装成功,恭喜~2.使用本地集群的官方提供了两种使用GORM来使用CockroachDB的方式:一种是CockroachDBServerless(测试阶段),另一种是使用本地集群。第一种方法需要注册一个CockroachDB云账号,然后按照官方提示创建链接。本文将使用第二种方式:运行cockroachstart-single-node命令:cockroachstart-single-node--advertise-addr'localhost'--insecure通过添加--insecure参数启动一个不安全的单节点集群。在SQLShell的欢迎文本中记录以下连接信息:CockroachDBnodestartingat2022-07-1413:21:35.179273246+0000UTC(took1.6s)build:CCLv22.1.3@2022/07/1114:04:38(go1.17.11)webui:http://localhost:8080sql:postgresql://root@localhost:26257/defaultdb?sslmode=disablesql(JDBC):jdbc:postgresql://localhost:26257/defaultdb?sslmode=disable&user=rootRPCclientflags:cockroach--host=localhost:26257--insecurelogs:/home/yuzhou/cockroach-data/logstempdir:/home/yuzhou/cockroach-data/cockroach-temp2801178939外部I/O路径:/home/yuzhou/cockroach-data/externstore[0]:path=/home/yuzhou/cockroach-datastorageengine:pebbleclusterID:43919fea-32cd-48f9-b585-713731d43ee9status:restartedpre-existingnodenodeID:1其中,postgresql://root@localhost:26257/defaultdb?sslmode=disable,通过SQL连接字符串连接到我们的CockroachDB集群。PS:此信息需要添加到后续的数据库连接datebase.go文件中,请注意。三、项目介绍1、项目功能本文创建的应用是一个简单的RESTfulAPI服务器:一个在线书店,将开放对书籍的访问和操作,主要包括以下功能:创建一本书获取一本书列表获取一本书information更新已有图书信息删除图书2.API接口规范为了实现上述功能,我们的应用需要对外提供如下API接口规范:创建图书:通过/book响应一个有效的POST请求。获取书籍列表:通过/books响应有效的GET请求。获取一本书:通过/book/{id}响应相应的GET请求。更新一本书:通过/book/{id}响应相应的PUT请求。删除一本书:通过/book/{id}响应相应的DELETE请求。{id}可以有效地识别一本书。26859123在豆瓣书本链接https://book.douban.com/subject/26859123/可以对应书《Go程序设计语言(英文版)》。3.项目结构本文将创建一个非常简单的应用结构,也就是本文使用的mybook应用的项目结构:.├──go.mod├──go.sum├──handlers.go├──主要的。go└──model├──database.go└──model.go4、安装项目依赖安装goget-ugithub.com/gorilla/mux,如下。安装goget-ugorm.io/gorm。安装goget-ugorm.io/driver/postgres。4、应用功能设计首先创建一个mybook目录(应用文件夹),然后使用gomodinitmybook。然后在里面创建我们的model文件夹,然后在model文件夹下新建一个model.go文件。1.model.go我们需要设计一个书籍模型,它将使用GORM创建。编写我们的model.go函数:packagemodelimport("gorm.io/gorm")typeBookstruct{gorm.ModelIDstring`json:"id"`Namestring`json:"name"`Authorstring`json:"author"`Descriptionstring`json:"description"`Pricefloat64`json:"price"`Categorystring`json:"category"`}如上图简单设计的图书结构包括id(ID),title(Name)、作者(Author)、图书描述(Description)、价格(Price)和类别(Category)。2.database.go这个程序会创建一个数据库连接,需要使用postgresql://root@localhost:26257/defaultdb?sslmode=disable进入gorm.Open()的连接参数,代码如下:modelimport("log""time""gorm.io/driver/postgres""gorm.io/gorm")funcSetupDB()(*gorm.DB,error){dsn:="postgresql://root@localhost:26257/defaultdb?sslmode=disable"db,err:=gorm.Open(postgres.Open(dsn),&gorm.Config{})iferr!=nil{log.Fatal("无法连接到数据库",err)}varnowtime.Timedb.Raw("SELECTNOW()").Scan(&now)log.Println(now)iferr=db.AutoMigrate(&Book{});err!=nil{log.Println(err)}returndb,err}3.返回handler.go到mybook文件夹,创建handler.go:packagemainimport("encoding/json""net/http""mybook/model""github.com/gorilla/mux""gorm.io/gorm")typeServerstruct{db*gorm.DB}typeUpdateBookstruct{Pricefloat64`json:"price"`描述字符串`json:"decription"`类别字符串`json:"category"`}funcNewServer(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")路由器。HandleFunc("/book/{id}",s.updateBook).Methods("PUT")router.HandleFunc("/book/{id}",s.deleteBook).Methods("DELETE")}func(s*Server)getBooks(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json;charset=UTF-8")varbooks[]model.Book如果错误:=s.db.Find(&books).Error;err!=nil{http.Error(w,err.Error(),http.StatusInternalServerError)返回}w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(books)}func(s*Server)createBook(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json;charset=UTF-8")varbookmodel.Book如果错误:=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}如果出错:=s.db.Create(newBook).错误;err!=nil{http.Error(w,err.Error(),http.StatusInternalServerError)返回}w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(newBook)}func(s*Server)getBook(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json;charset=UTF-8")varbookmodel.Bookvars:=mux.Vars(r)id:=vars["id"]iferr:=s.db.Where("id=?",id).First(&book).Error;err!=nil{http.Error(w,err.Error(),http.StatusNotFound)返回}w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(book)}func(s*Server)updateBook(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json;charset=UTF-8")varupdateBookUpdateBookvarbookmodel.Bookvars:=mux.Vars(r)id:=vars["id"]复制代码如果错误:=json.NewDecoder(r.Body).Decode(&updateBook);err!=nil{http.Error(w,err.Error(),http.StatusInternalServerError)return}iferr:=s.db.Where("id=?",id).First(&book).Error;err!=nil{http.Error(w,err.Error(),http.StatusNotFound)return}iferr:=s.db.Model(&book).Updates(&model.Book{价格:updateBook.Price,描述:updateBook.Description,类别:updateBook.Category})。错误;err!=nil{http.Error(w,err.Error(),http.StatusInternalServerError)返回}w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode(book)}func(s*Server)deleteBook(whttp.ResponseWriter,r*http.Request){w.Header().Set("Content-Type","application/json;charset=UTF-8")varbookmodel.Bookvars:=mux.Vars(r)id:=vars["id"]iferr:=s.db.Where("id=?",id).First(&book).Error;err!=nil{http.Error(w,err.Error(),http.StatusNotFound)return}iferr:=s.db.Delete(&book).Error;err!=nil{http.Error(w,err.Error(),http.StatusInternalServerError)return}w.WriteHeader(http.StatusOK)json.NewEncoder(w).Encode("图书删除成功!")}4.main.go在main.go中,我们初始化数据库,创建一个路由器实例,将变量db作为参数传递给我们的服务器初始化,然后将路由器实例作为参数传递给方法registryRouter()然后,我们使用监听器运行服务器。packagemainimport("log""net/http""github.com/gorilla/mux""mybook/model")funcmain(){db,err:=model.SetupDB()iferr!=nil{log.Println("Failedsettingupdatabase")}router:=mux.NewRouter()server:=NewServer(db)server.RegisterRouter(router)log.Fatal(http.ListenAndServe(":8000",router))}完成后运行所有程序,运行gorun。命令,启动成功如下:此时运行cockcoachsql进入数据库命令行,然后运行showtables;命令,可以看到在默认数据库defaultdb中生成了一张books数据库表(与我们的模型books同名),如下图:我们执行select*frombooks查看我们生成的表信息:可以看到生成了我们定义的字段id、name、author、description、price、category,GORM还帮助默认添加了created_at(创建时间)、updated_at(更新时间)和deleted_at(删除时间)三个字段。5.API测试为了方便我们测试本书应用的API功能是否正常,我们将使用API??fox工具进行测试。首先在数据库中添加一条新记录,如下:INSERTINTObooks(id,name,author,description,price,category)VALUES(1,'Go编程语言(英文版)','AlanA.A.Donovan(艾伦A.A.多诺万)/布赖恩·W·凯米汉(BrianW.Kemighan)','被称为Go语言圣经的书非常值得一读',79.00,'Golang');获取书籍列表:/bookstest。插入成功,我们访问后台http://127.0.0.1:8000/books路径,可以看到如下成功,说明获取图书列表的API成功,恭喜。接下来,为了测试,下载Linux版的Apifox,帮助我们快速测试其他接口。获取一本书:/book/1测试。其他接口测试类似。总结本文使用Go语言中非常实用的GorillaMux和GORM库,结合分布式CockroachDB数据库编写了一个简单的书籍RestfulAPI,最后通过Apifox测试工具验证了服务端API的正确性。学习了CockroachDB数据的安装,学习了GorillaMux和GORM的使用。显然,这篇文章还有很多不足,比如没有充分利用CockroachDB数据库的分布式特性,没有为API编写测试代码,测试不一定完美。这些可以给读者一些优化思路。了解更多开源知识,请访问:开源基础软件社区https://ost.51cto.com。