基于Go语言构建RESTful API服务


Posted in Golang onJuly 25, 2021

在实际开发项目中,你编写的服务可以被其他服务使用,这样就组成了微服务的架构;也可以被前端调用,这样就可以前后端分离。那么,本文主要介绍什么是 RESTful API,以及 Go 语言是如何玩转 RESTful API 的。

什么是 RESTful API

RESTful API 是一套规范,它可以规范我们如何对服务器上的资源进行操作。在了解 RESTful API 之前,我先为你介绍下 HTTP Method,因为 RESTful API 和它是密不可分的。

说起 HTTP Method,最常见的就是POST和GET,其实最早在 HTTP 0.9 版本中,只有一个GET方法,该方法是一个幂等方法,用于获取服务器上的资源,也就是我们在浏览器中直接输入网址回车请求的方法。

在 HTTP 1.0 版本中又增加了HEAD和POST方法,其中常用的是 POST 方法,一般用于给服务端提交一个资源,导致服务器的资源发生变化。

随着网络越来越复杂,发现这两个方法是不够用的,就继续新增了方法。所以在 HTTP1.1 版本的时候,一口气增加到了 9 个,新增的方法有 HEAD、OPTIONS、PUT、DELETE、TRACE、PATCH 和 CONNECT。下面我为你一一介绍它们的作用。

  • GET 方法可请求一个指定资源的表示形式,使用 GET 的请求应该只被用于获取数据。
  • HEAD 方法用于请求一个与 GET 请求的响应相同的响应,但没有响应体。
  • POST 方法用于将实体提交到指定的资源,通常导致服务器上的状态变化或副作用。
  • PUT 方法用于请求有效载荷替换目标资源的所有当前表示。
  • DELETE 方法用于删除指定的资源。
  • CONNECT 方法用于建立一个到由目标资源标识的服务器的隧道。
  • OPTIONS 方法用于描述目标资源的通信选项。
  • TRACE 方法用于沿着到目标资源的路径执行一个消息环回测试。
  • PATCH 方法用于对资源应用部分修改。

从以上每个方法的介绍可以看到,HTTP 规范针对每个方法都给出了明确的定义,所以我们使用的时候也要尽可能地遵循这些定义,这样我们在开发中才可以更好地协作。

理解了这些 HTTP 方法,就可以更好地理解 RESTful API 规范了,因为 RESTful API 规范就是基于这些 HTTP 方法规范我们对服务器资源的操作,同时规范了 URL 的样式和 HTTP Status Code。

在 RESTful API 中,使用的主要是以下五种 HTTP 方法:

  • GET,表示读取服务器上的资源;
  • POST,表示在服务器上创建资源;
  • PUT,表示更新或者替换服务器上的资源;
  • DELETE,表示删除服务器上的资源;
  • PATCH,表示更新 / 修改资源的一部分。

以上 HTTP 方法在 RESTful API 规范中是一个操作,操作的就是服务器的资源,服务器的资源通过特定的 URL 表示。

现在我们通过一些示例让你更好地理解 RESTful API,如下所示:

HTTP GET http://localhost:1000/users

HTTP GET http://localhost:1000/user/123

以上是两个 GET 方法的示例:

  • 第一个表示获取所有用户的信息
  • 第二个表示获取 ID 为 123 用户的信息

下面再看一个 POST 方法的示例,如下所示:

HTTP POST http://localhost:1000/user

这个示例表示创建一个用户,通过 POST 方法给服务器提供创建这个用户所需的全部信息。

现在你已经知道了如何创建一个用户,那么如果要更新某个特定的用户怎么做呢?其实也非常简单,示例代码如下所示:

HTTP PUT http://localhost:1000/user/123

这表示要更新 / 替换 ID 为 123 的这个用户,在更新的时候,会通过 PUT 方法提供更新这个用户需要的全部用户信息。这里 PUT 方法和 POST 方法不太一样的是,从 URL 上看,PUT 方法操作的是单个资源,比如这里 ID 为 123 的用户。

看到这里,相信你已经知道了如何删除一个用户,示例代码如下所示:

HTTP DELETE http://localhost:1000/user/123

DELETE 方法的使用和 PUT 方法一样,也是操作单个资源,这里是删除 ID 为 123 的这个用户。

一个简单的 RESTful API

相信你已经非常了解什么是 RESTful API 了,现在开始,我会带你通过一个使用 Golang 实现 RESTful API 风格的示例,加深 RESTful API 的理解。

Go 语言的一个很大的优势,就是可以很容易地开发出网络后台服务,而且性能快、效率高。在开发后端 HTTP 网络应用服务的时候,我们需要处理很多 HTTP 的请求访问,比如常见的RESTful API 服务,就要处理很多 HTTP 请求,然后把处理的信息返回给使用者。对于这类需求,Golang 提供了内置的 net/http 包帮我们处理这些 HTTP 请求,让我们可以比较方便地开发一个 HTTP 服务。

下面我们来看一个简单的 HTTP 服务的 Go 语言实现,代码如下所示:

func main() {

   http.HandleFunc("/users",handleUsers)

   http.ListenAndServe(":1000", nil)

}

func handleUsers(w http.ResponseWriter, r *http.Request){

   fmt.Fprintln(w,"ID:1,Name:张三")

   fmt.Fprintln(w,"ID:2,Name:李四")

   fmt.Fprintln(w,"ID:3,Name:王五")

}

这个示例运行后,你在浏览器中输入http://localhost:1000/users, 就可以看到如下内容信息:

ID:1,Name:张三

ID:2,Name:李四

ID:3,Name:王五

也就是获取所有的用户信息,但是这并不是一个 RESTful API,因为使用者不仅可以通过 HTTP GET 方法获得所有的用户信息,还可以通过 POST、DELETE、PUT 等 HTTP 方法获得所有的用户信息,这显然不符合 RESTful API 的规范。

现在我对以上示例进行修改,使它符合 RESTful API 的规范,修改后的示例代码如下所示:

func handleUsers(w http.ResponseWriter, r *http.Request){

   switch r.Method {

   case "GET":

      w.WriteHeader(http.StatusOK)

      fmt.Fprintln(w,"ID:1,Name:张三")

      fmt.Fprintln(w,"ID:2,Name:李四")

      fmt.Fprintln(w,"ID:3,Name:王五")

   default:

      w.WriteHeader(http.StatusNotFound)

      fmt.Fprintln(w,"not found")
   }
}

这里我只修改了 handleUsers 函数,在该函数中增加了只在使用 GET 方法时,才获得所有用户的信息,其他情况返回 not found。

现在再运行这个示例,会发现只能通过 HTTP GET 方法进行访问了,使用其他方法会提示 not found。

RESTful JSON API

在项目中最常见的是使用 JSON 格式传输信息,也就是我们提供的 RESTful API 要返回 JSON 内容给使用者。

同样用上面的示例,我把它改造成可以返回 JSON 内容的方式,示例代码如下所示:

//数据源,类似MySQL中的数据

var users = []User{

   {ID: 1,Name: "张三"},

   {ID: 2,Name: "李四"},

   {ID: 3,Name: "王五"},

}

func handleUsers(w http.ResponseWriter, r *http.Request){

   switch r.Method {

   case "GET":

      users,err:=json.Marshal(users)

      if err!=nil {

         w.WriteHeader(http.StatusInternalServerError)

         fmt.Fprint(w,"{\"message\": \""+err.Error()+"\"}")

      }else {

         w.WriteHeader(http.StatusOK)

         w.Write(users)

      }

   default:

      w.WriteHeader(http.StatusNotFound)

      fmt.Fprint(w,"{\"message\": \"not found\"}")

   }

}

//用户

type User struct {

   ID int

   Name string

}

从以上代码可以看到,这次的改造主要是新建了一个 User 结构体,并且使用 users 这个切片存储所有的用户,然后在 handleUsers 函数中把它转化为一个 JSON 数组返回。这样,就实现了基于 JSON 数据格式的 RESTful API。

运行这个示例,在浏览器中输入http://localhost:1000/users,可以看到如下信息:

[{"ID":1,"Name":"张三"},{"ID":2,"Name":"李四"},{"ID":3,"Name":"王五"}]

这已经是 JSON 格式的用户信息,包含了所有用户。

Gin 框架

虽然 Go 语言自带的 net/http 包,可以比较容易地创建 HTTP 服务,但是它也有很多不足:

  • 不能单独地对请求方法(POST、GET 等)注册特定的处理函数
  • 不支持 Path 变量参数
  • 不能自动对 Path 进行校准
  • 性能一般
  • 基于以上这些不足,出现了很多 Golang Web 框架,如 Mux,Gin、Fiber 等,今天我要为你介绍的就是这款使用最多的 Gin 框架。

引入 Gin 框架

Gin 框架是一个在 Github 上开源的 Web 框架,封装了很多 Web 开发需要的通用功能,并且性能也非常高,可以让我们很容易地写出 RESTful API。

Gin 框架其实是一个模块,也就是 Go Mod,所以采用 Go Mod 的方法引入即可。首先需要下载安装 Gin 框架,安装代码如下:

go get -u github.com/gin-gonic/gin

然后就可以在 Go 语言代码中导入使用了,导入代码如下:

import "github.com/gin-gonic/gin"

通过以上安装和导入这两个步骤,就可以在你的 Go 语言项目中使用 Gin 框架了。

使用 Gin 框架

现在,已经引入了 Gin 框架,下面我就是用 Gin 框架重写上面的示例,修改的代码如下所示:

func main() {

   r:=gin.Default()

   r.GET("/users", listUser)

   r.Run(":1000")

}

func listUser(c *gin.Context)  {

   c.JSON(200,users)

}

相比 net/http 包,Gin 框架的代码非常简单,通过它的 GET 方法就可以创建一个只处理 HTTP GET 方法的服务,而且输出 JSON 格式的数据也非常简单,使用 c.JSON 方法即可。

最后通过 Run 方法启动 HTTP 服务,监听在 1000 端口。现在运行这个 Gin 示例,在浏览器中输入http://localhost:1000/users,看到的信息和通过 net/http 包实现的效果是一样的。

新增一个用户

现在你已经可以使用 Gin 获取所有用户,还可以获取特定的用户,那么你也应该知道如何新增一个用户了,现在我通过 Gin 实现如何新增一个用户,看和你想的方案是否相似。

根据 RESTful API 规范,实现新增使用的是 POST 方法,并且 URL 的格式为http://localhost:1000/users,向这个 URL 发送数据,就可以新增一个用户,然后返回创建的用户信息。

现在我使用 Gin 框架实现新增一个用户,示例代码如下:

func main() {

   //省略没有改动的代码

   r.POST("/users", createUser)

}

func createUser(c *gin.Context) {

   name := c.DefaultPostForm("name", "")

   if name != "" {

      u := User{ID: len(users) + 1, Name: name}

      users = append(users, u)

      c.JSON(http.StatusCreated,u)

   } else {

      c.JSON(http.StatusOK, gin.H{

         "message": "请输入用户名称",

      })

   }

}

以上新增用户的主要逻辑是获取客户端上传的 name 值,然后生成一个 User 用户,最后把它存储到 users 集合中,达到新增用户的目的。

在这个示例中,使用 POST 方法来新增用户,所以只能通过 POST 方法才能新增用户成功。

现在运行这个示例,然后通过如下命令发送一个新增用户的请求,查看结果:

➜ curl -X POST -d 'name=Ele' http://localhost:1000/users

{"ID":4,"Name":"Ele"}

可以看到新增用户成功,并且返回了新增的用户,还有分配的 ID。

获取特定的用户

现在你已经掌握了如何使用 Gin 框架创建一个简单的 RESTful API,并且可以返回所有的用户信息,那么如何获取特定用户的信息呢?

下面我通过 Gin 框架 Path 路径参数来实现获得特定用户的信息功能,示例代码如下:

func main() {

   //省略没有改动的代码

   r.GET("/users/:id", getUser)

}

func getUser(c *gin.Context) {

   id := c.Param("id")

   var user User

   found := false

   //类似于数据库的SQL查询

   for _, u := range users {

      if strings.EqualFold(id, strconv.Itoa(u.ID)) {

         user = u

         found = true

         break

      }

   }

   if found {

      c.JSON(200, user)

   } else {

      c.JSON(404, gin.H{

         "message": "用户不存在",

      })

   }

}

在 Gin 框架中,路径中使用冒号表示 Path 路径参数,比如示例中的 :id,然后在 getUser 函数中可以通过 c.Param("id") 获取需要查询用户的 ID 值。PS:Param 方法的参数要和 Path 路径参数中的一致,比如示例中都是 ID。

现在运行这个示例,通过浏览器访问http://localhost:1000/users/4,就可以获得 ID 为 4 的用户,输出信息如下所示:

{"ID":4,"Name":"Ele"}

可以看到,已经正确的获取到了 ID 为 4 的用户,他的名字叫Ele。

假如我们访问一个不存在的 ID,会得到什么结果呢?比如 99,示例如下所示:

curl http://localhost:1000/users/99

{"message":"用户不存在"}

从以上示例输出可以看到,返回了『用户不存在』的信息,和我们代码中处理的逻辑一样。

总结

Go 语言已经给我们提供了比较强大的 SDK,让我们可以很容易地开发网络服务的应用,而借助第三方的 Web 框架,可以让这件事情更容易、更高效。比如这篇文章介绍的 Gin 框架,就可以很容易让我们开发出 RESTful API。在我们做项目开发的时候,要善于借助已经有的轮子,让自己的开发更有效率,也更容易实现。

到此这篇关于基于Go语言构建RESTful API服务的文章就介绍到这了,更多相关Go语言构建RESTful API 内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Golang 相关文章推荐
goland 恢复已更改文件的操作
Apr 28 Golang
golang json数组拼接的实例
Apr 28 Golang
对Golang中的FORM相关字段理解
May 02 Golang
golang日志包logger的用法详解
May 05 Golang
go设置多个GOPATH的方式
May 05 Golang
Go 语言下基于Redis分布式锁的实现方式
Jun 28 Golang
golang操作rocketmq的示例代码
Apr 06 Golang
Go语言的协程上下文的几个方法和用法
Apr 11 Golang
golang定时器
Apr 14 Golang
GO语言字符串处理函数之处理Strings包
Apr 14 Golang
在ubuntu下安装go开发环境的全过程
Aug 05 Golang
go goroutine 怎样进行错误处理
Jul 16 #Golang
go开发alertmanger实现钉钉报警
Jul 16 #Golang
K8s部署发布Golang应用程序的实现方法
Jul 16 #Golang
入门学习Go的基本语法
Jul 07 #Golang
golang中字符串MD5生成方式总结
Jul 04 #Golang
golang fmt格式“占位符”的实例用法详解
Jul 04 #Golang
Go语言空白表示符_的实例用法
Jul 04 #Golang
You might like
用PHP连接MySQL代码的参数说明
2008/06/07 PHP
利用php绘制饼状图的实现代码
2013/06/07 PHP
php ucwords() 函数将字符串中每个单词的首字符转换为大写(实现代码)
2016/05/12 PHP
PHP实现冒泡排序的简单实例
2016/05/26 PHP
PHP中Cookie的使用详解(简单易懂)
2017/04/28 PHP
PHP微信开发之微信录音临时转永久存储
2018/01/26 PHP
php将从数据库中获得的数据转换成json格式并输出的方法
2018/08/21 PHP
YUI的Tab切换实现代码
2010/04/11 Javascript
上传的js验证(图片/文件的扩展名)
2013/04/25 Javascript
extjs中form与grid交互数据(record)的方法
2013/08/29 Javascript
一张表格告诉你windows.onload()与$(document).ready()的区别
2014/05/16 Javascript
Jquery常用的方法汇总
2015/09/01 Javascript
javascript中去除数组重复元素的实现方法【实例】
2016/04/12 Javascript
JS简单获取及显示当前时间的方法
2016/08/03 Javascript
浅析script标签中的defer与async属性
2016/11/30 Javascript
使用openSpeDiv方法实现Ecshop登录弹窗框效果
2017/03/13 Javascript
javascript 日期相减-在线教程(附代码)
2017/08/17 Javascript
Vue+mui实现图片的本地缓存示例代码
2018/05/24 Javascript
Angular6封装http请求的步骤详解
2018/08/13 Javascript
JavaScript实现多张图片放大镜效果示例【不限定图片尺寸,rem单位】
2019/05/14 Javascript
了解JavaScript表单操作和表单域
2019/05/27 Javascript
vue 实现单选框设置默认选中值
2019/11/07 Javascript
使用Mock.js生成前端测试数据
2020/12/13 Javascript
[04:09]2018年度DOTA2社区贡献奖-完美盛典
2018/12/16 DOTA
采用Psyco实现python执行速度提高到与编译语言一样的水平
2014/10/11 Python
使用Kivy将python程序打包为apk文件
2017/07/29 Python
Python中property属性实例解析
2018/02/10 Python
TensorFlow神经网络优化策略学习
2018/03/09 Python
python打印9宫格、25宫格等奇数格 满足横竖斜相加和相等
2019/07/19 Python
基于python的selenium两种文件上传操作实现详解
2019/09/19 Python
python自动化测试三部曲之unittest框架的实现
2020/10/07 Python
巴西Mr. Cat在线商店:购买包包和鞋子
2019/09/08 全球购物
日语专业毕业生自荐信
2013/11/11 职场文书
计算机专业应届生求职信
2014/04/06 职场文书
地陪导游欢迎词
2015/01/26 职场文书
Golang原生rpc(rpc服务端源码解读)
2022/04/07 Golang