在实际项目开发中,编写的服务可以被其他服务使用,这样就组成了微服务的架构;也可以被前端调用,这样就可以使前后端分离。
什么是 RESTful API
RESTful API 是一套规范,它可以规范我们如何对服务器上的资源进行操作。在了解它之前,需要先了解 HTTP Method,因为 RESTful API和它是密不可分的。
说起 HTTP Method,最常见的就是POST 和 GET,其实最早在 HTTP 0.9 版本,只有一个GET方法,该方法是一个幂等方法,用于获取服务器上的资源,也就是我们在浏览器中直接输入网址回车请求的方法。 在 HTTP1.0版本中又增加 HEAD和POST方法,其中常用的是 POST 方法,一般用于给服务端提交一个资源,导致服务器的资源发生变化。 但随着网络越来越复杂,发现这两个方法是不够用的,就继续新增了方法。 所以在 HTTP1.1版本时,一口气增加到9个,新增的方法有 HEAD、OPTIONS、PUT、DELETE、TRACE、PATCH和CONNECT。
1. GET 方法可请求一个指定资源的表示形式,使用GET请求应该只被用于获取数据。
1. HEAD 方法用于请求一个与GET请求的响应相同的响应,但没有响应体。
1. POST 方法用于将实体提交到指定的资源,通常导致服务器上的状态变化或副作用。
1. PUT 方法用于请求有效载荷替换目标资源的所有当前表示。
1. DELETE 方法用于删除指定的资源。
1. CONNECT 方法用于建立一个到由目标资源标识的服务器的隧道。
1. OPTIONS 方法用于描述目标资源的通信选项。
1. TRACE 方法用于沿着到目标资源的路径执行一个消息环回测试。
1. PATCH 方法用于对资源应用部分修改。
从每个方法的介绍来看,HTTP 规范针对每个方法都给出了明确的定义,所以我们使用的时候要尽可能的遵循这些定义,这样在开发中我们才可以更好的协作。 理解了这些 HTTP方法,就可以更好的理解 RESTful API 规范了,因为RESTful API 规范就是基于这些 HTTP方法规范我们对服务器资源的操作,同时规范了 URL 的样式和 HTTP Status Code。
在 RESTful API 中,使用的主要是一下五种 HTTP 方法:
1. GET,表示读取服务器上的资源
1. POST, 表示在服务器上创建资源
1. PUT, 表示更新或者替换服务器上的资源
1. DELETE,表示删除服务器上的资源
1. PATCH,表示更新/ 修改资源的一部分
这些HTTP 方法在 RESTful API 规范中是一个操作,操作的就是服务器的资源,服务器的资源通过特定的URL表示。示例如下:
// 表示获取所有用户信息
HTTP GET https://www.test.org/users
// 获取ID为111用户的信息
HTTP GET https://www.test.org/users/111
// 创建一个用户,通过POST 方法给服务器提供创建这个用户所需的全部信息。注意:这里 users 是复数
HTTP POST https://www.test.org/users
// 更新特定用户用 PUT,更新时 PUT 方法提供更新这个用户需要的全部用户信息, PUT 方法和 POST 方法不太一样的是, 从 URL上看,PUT 方法操作的是单个资源。 如果更新一个用户的部分信息,使用 PATCH 方法更恰当。
HTTP PUT https://www.test.org/users/111
// 删除一个用户, DELETE 方法和 PUT方法一样, 都是操作单个资源
HTTP DELETE https://www.test.org/users/111
一个简单的 RESTful API
我们已经知道什么是 RESTful API 了,接下来我们一起使用Golang实现 RESTful API 风格示例,加深对它的理解。 Go语言的一个很大的优势,就是可以很容易的开发出网络后太服务,而且性能快、效率高。在开发后端 HTTP 网络应用服务的时候,需要处理很多 HTTP请求,然后把处理信息返回给调用者。对于这类需求,Golang 提供了内置的 net/http 包帮我们处理这些 HTTP 请求,让我们可以比较方便的开发一个 HTTP 服务。示例如下:
func main() {
http.HandleFunc("/users", handleUsers)
http.ListenAndServe(":8080", nil)
}
func handleUsers(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "ID :1, Name:1")
fmt.Fprintln(w, "ID :2, Name:2")
fmt.Fprintln(w, "ID :3, Name:3")
}
// 程序运行后,在浏览器输入 http://localhost:8080/users,就可以得到一下信息:
ID :1, Name:1
ID :2, Name:2
ID :3, Name:3
// 获取了所以信息,但是并不是一个 RESTful API,因为使用者不仅可以通过 HTTP GET方法获得所有用户信息,还可以通过POST、DELETE、PUT 等方法获得所有用户信息,这显然不符合 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:1")
fmt.Fprintln(w, "ID :2, Name:2")
fmt.Fprintln(w, "ID :3, Name:3")
default:
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "not found")
}
}
RESTful JSON API
项目中最常用的是 JSON 格式传输信息,也就是说我们要提供的 RESTful API 要返回 JSON 内容给调用者,改造上述示例:
type user struct{
ID int
Name string
}
users := []user{
{ID : 1, Name: "1"},
{ID : 2, Name: "2"},
{ID : 3, Name: "3"},
}
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.Fprintln(w, "{\"message\": \""+err.Error()+"\"}")
} else {
w.WriteHeader(http.StatusOk)
w.Write(users)
}
default:
w.WriteHeader(http.StatusNotFound)
fmt.Fprintln(w, "{\"message\": \"not found\"}")
}
}
// 这样就实现了基于JSON 数据格式的 RESTful API。
Gin框架
虽然Go语言自带的 net/http包,可以比较容易的创建HTTP服务,但是它有很多不足:
1. 不能单独的对请求方法(POST、GET等)注册特定的处理函数;
1. 不支持 Path 变量参数;
1. 不能自动对Path进行校准;
1. 性能一般;
1. 扩展性不足
基于这些不足点,出现了很多Golang Web框架,如 Mux、Gin、Fiber等,我们要学习的是用的最多的Gin框架。
引入Gin框架
Gin框架是一个在 Github 上开源的Web框架,封装了很多 Web 开发需要的通用功能,并且性能也非常高,可以让我们很容易写出 RESTful API。Gin框架其实是一个模块,也就是Go Mod,所以采用 Go Mod 的方法引入即可。先下载安装 Gin 框架,在导入使用即可,这样就可以在项目中使用 Gin框架了。
go get -u github.com/gin-gonic/gin
import "githun.com/gin-gonic/gin"
使用Gin框架
现在我们已经引入Gin框架,我们尝试用Gin框架改写上面的例子。
func main() {
r := gin.Default()
r.GET("/users", listUser)
r.Run(":8080")
}
func listUser(c *gin.Context) {
c.JSON(200, users)
}
// 相比 net/http包,Gin框架的代码非常简单,通过它的GET 方法可以创建一个只处理 HTTP GET 方法的服务,而且输出 JSON 格式数据也非常简单,使用 c.JSON 方法即可。
// 最后通过 Run 方法启动 HTTP服务,监听8080端口。
获取特定用户
http://localhost:8080/users/111
// 通过 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 string.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
提示: Param 方法的参数要和 Path路径参数中的一致,比如示例中都是ID。
新增一个用户
// 使用POST 方法即可
func main() {
r.POST("/users", createUser)
}
func createUser(c *gin.Context) {
name := c.DefaultPostForm("name", "")
if name != "" {
u := user{
ID : len(user) +1,
Name : name
}
users = append(users, u)
c.JSON(http.StatusCreated, u)
} else {
c.JSON(http.StatusOK, gin.H{
"message" : "请输入用户名"
})
}
}
总结
Go语言已经给我们提供了比较强大的SDK,让我们可以很容易的开发网络服务应用,而借助第三方的 Web框架,可以让这件事情更容易、更高效。Gin框架,可以很容易让我们开发出 RESTful API。