构建API接口
使用标准库net/http
Go语言中net/http标准库提供了HTTP客户端和服务端的实现,可以用于创建HTTP请求、处理HTTP响应、构建Web服务器等。下面使用该库编写一个接收HTTP请求并返回响应的Server端示例。
package main
import (
"net/http"
"fmt"
)
func say(w http.ReponseWriter,r *http.Request){
fmt.Fprintln(w,"hello world")
}
func main(){
http.HandleFunc("/",say)
err := http.ListenAndServe(":8080",nil)
if err != nil{
fmt.Println(err)
return
}
}
在这段代码中,http.HandleFunc("/",say)将请求路由为"/"的请求与say函数进行绑定。当客户端发送一个请求,请求的路径为”/"时,服务器会调用say函数来处理该请求。http.ListenAndServe(":8080",nil)用于创建一个HTTP服务器并在本机的8080端口上监听。say函数接收两个参数,http.ReponseWriter类型参数用于向客户端返回响应,*http.Request类型参数用于获取和处理请求。
使用go run main.go运行该函数后,打开电脑上的浏览器在地址栏输入127.0.0.1:8080回车,就能看到页面上出现"hello world"。
使用Gin框架
Gin是一个用于构建高性能Web应用程序的轻量级HTTP框架,它基于Go语言的net/http包进行封装,并提供了许多有用的功能和工具。
安装Gin
go get -u github.com/gin-gonic/gin
使用示例
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
c.JSON(200, gin.H{
"content": "hello world",
})
})
r.Run()
}
在这段代码中,首先使用gin.Default()创建一个默认的路由引擎r,r.GET接收两个参数,一个是路由地址,另一个是处理函数,表示当接收指定路由地址的GET请求时,则执行后面的处理函数。该示例中当访问"/hello"时,则会执行后面的匿名函数。c.JSON用于向客户端返回一个json格式的字符串。
r.Run()用于启动HTTP服务,默认是在8080端口监听。
使用go run main.go运行该函数后,打开电脑上的浏览器在地址栏输入127.0.0.1:8080/hello回车,就能看到一串json格式的字符串。
用户认证
在Gin框架中使用JWT
JWT(JSON Web Token)是一种用于在网络应用间传递信息的安全跨域解决方案。它规定了一种Token实现方式,目前多用于前后端分离项目和OAuth2.0业务场景下。
安装
go get github.com/golang-jwt/jwt/v4
自定义Claims
我们需要根据需求决定jwt中需要存储哪些数据。比如需要存储用户ID和用户名。
type MyClaims struct {
UserID int64 `json:"user_id"`
UserName string `json:"user_name"`
jwt.StandardClaims
}
生成JWT
const TokenExpireDuration = time.Hour * 24
var CustomSecret = []byte("春夏秋冬")
func GenToken(userID int64, userName string) (string, error) {
claims := MyClaims{
userID,
userName,
jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(TokenExpireDuration)),
Issuer: "my-project",
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString(CustomSecret)
}
解析JWT
func ParseToken(tokenString string) (*MyClaims, error) {
var mc = new(MyClaims)
token, err := jwt.ParseWithClaims(tokenString, mc, func(token *jwt.Token) (interface{}, error) {
return CustomSecret, nil
})
if err != nil {
return nil, err
}
if token.Valid {
return mc, nil
}
return nil, errors.New("invalid token")
}
使用JWT
在Gin中,可以指定特定的路由,用于对外提供获取token的通道。
r.POST("/auth", authHandler)
func authHandler(c *gin.Context) {
if ... {
tokenString, _ := GenToken(userID,userName)
c.JSON(http.StatusOK, gin.H{"token": tokenString,}
}
}
用户通过上面的接口获取token后,后续就会携带token访问其它接口,这时候需要对这些请求中包含的token进行校验,以此决定是否允许被访问。由于访问其它所有接口都需要先进行token校验,因此可以使用中间件。这里假设携带的token放在请求头的Authorization中。
func JWTAuthMiddleware() func(c *gin.Context) {
return func(c *gin.Context) {
authHeader := c.Request.Header.Get("Authorization")
if authHeader == "" {
c.JSON(http.StatusOK, gin.H{"msg": "请求头中auth为空",}
c.Abort()
return
}
parts := strings.SplitN(authHeader, " ", 2)
if !(len(parts) == 2 && parts[0] == "Bearer") {
c.JSON(http.StatusOK, gin.H{"msg": "请求头中auth格式有误",}
c.Abort()
return
}
mc, err := jwt.ParseToken(parts[1])
if err != nil {
c.JSON(http.StatusOK, gin.H{"msg": "无效的token",}
c.Abort()
return
}
// 将当前请求的userID信息保存到请求的上下文中
c.Set("userID", mc.UserID)
c.Next()
}
}