在讲述框架之前,先来说说Go语言的内置net/http包,其实net/http已经为我们提供了基础的路由函数组合和丰富的功能函数,如果你只是需要简单的API编写,net/http就完全足够了。一个简单的web服务程序:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
// 该方法接收一个路由匹配的字符串,以及一个 func(ResponseWriter, *Request) 类型的函数
http.HandleFunc("/", handler)
log.Fatal(http.ListenAndServe(":8000", nil)) // 监听本地8000端口
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) // r.URL.Path 输出url的路径
}
浏览器访问结果:
来进阶一下,看看如何解析常见的请求参数类型,以及如何返回json格式。
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
)
func main() {
// 该方法接收一个路由匹配的字符串,以及一个 func(ResponseWriter, *Request) 类型的函数
http.HandleFunc("/", handler)
http.HandleFunc("/get", handleGet)
http.HandleFunc("/postJson", handlePostJson)
http.HandleFunc("/postForm", handlePostForm)
http.HandleFunc("/responseJson", handleResponseJson)
log.Fatal(http.ListenAndServe(":8000", nil)) // 监听本地8000端口
}
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintf(w, "URL.Path = %q\n", r.URL.Path) // r.URL.Path 输出url的路径
}
// 处理GET请求查询参数
func handleGet(w http.ResponseWriter, r *http.Request) {
query := r.URL.Query()
id := query.Get("id")
fmt.Fprintf(w, "GET: id=%s\n", id)
}
// 处理application/json类型的POST请求
func handlePostJson(w http.ResponseWriter, r *http.Request) {
// 根据请求body创建一个json解析器实例
decoder := json.NewDecoder(r.Body)
// 用于存放参数key=value数据
var params map[string]string
// 解析参数 存入map
decoder.Decode(¶ms)
fmt.Fprintf(w, "POST json: username=%s, password=%s\n", params["username"], params["password"])
}
// 处理application/x-www-form-urlencoded类型的POST请求
func handlePostForm(w http.ResponseWriter, r *http.Request) {
r.ParseForm()
username := r.Form.Get("username")
password := r.Form.Get("password")
fmt.Fprintf(w, "POST form-urlencoded: username=%s, password=%s\n", username, password)
}
// 返回JSON数据格式
func handleResponseJson(w http.ResponseWriter, r *http.Request) {
type Response struct {
Code int `json:"code"`
Msg string `json:"msg"`
Data interface{} `json:"data"`
}
res := Response{
200,
"success",
"admin",
}
json.NewEncoder(w).Encode(res) // 关键
}
/get
/postJson
/postForm
/responseJson
net/http
gin框架httprouter
KoaRestful API
1.快速开始
package main
import "github.com/gin-gonic/gin"
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(200, gin.H{
"message": "pong",
})
})
r.Run() // 默认监听本地8080端口,如果需要更改可以使用 r.Run(":9000")
}
浏览器访问:控制台输出:
在这段代码中gin做了什么?
gin.Default()rr.Get("/ping", ...)r.Run()r.Run(":9999")
2.路由
GETPOSTPUTPATCHDELETEOPTIONSAny
r.GET("/someGet", func)
r.POST("/somePost", func)
r.PUT("/somePut", func)
r.DELETE("/someDelete", func)
r.PATCH("/somePatch", func)
r.HEAD("/someHead", func)
r.OPTIONS("/someOptions", func)
//处理所有的请求方法
r.Any("/any", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"请求类型": c.Request.Method,
})
})
路由参数
// 无参数
r.GET("/", func (c *gin.Context) {
c.String(http.StatusOK, "Hello")
})
// 路径参数,匹配 /path/admin
r.GET("/path/:name", func(c *gin.Context) {
name := c.Param("name")//取得URL路径中参数name的值
c.String(http.StatusOK, "Hello %s", name)
})
// 星号路由参数,匹配所有,不建议使用,如/all/*id
r.GET("/all/*id", func(c *gin.Context) {
id := c.Param("id")
c.String(http.StatusOK, "id is %s", id)
})
// 查询参数,匹配 user?name=xxx&role=xxx,role可选
r.GET("/user", func(c *gin.Context) {
name := c.Query("name")//查询请求URL后面的参数name的值
role := c.DefaultQuery("role", "teacher")//如果获取不到,会赋值默认值"teacher"
c.String(http.StatusOK, "%s is a %s", name, role)
})
// form表单
r.POST("/form", func(c *gin.Context) {
username := c.PostForm("username")
password := c.DefaultPostForm("password", "000000") // 可设置默认值
c.JSON(http.StatusOK, gin.H{
"username": username,
"password": password,
})
})
// json参数
r.POST("/json", func(c *gin.Context) {
type Body struct {
Email string `json:"email"`
Username string `json:"username"`
}
var body Body
c.ShouldBind(&body)//绑定参数,将参数解析到body结构体中
c.JSON(http.StatusOK, body)
})
// 数组参数,匹配多选业务如 array?answer=xxx&answer=xxx&answer=xxx,key一样,value不同
r.GET("/array", func(c *gin.Context) {
array := c.QueryArray("answer")
c.JSON(http.StatusOK, array)
})
// map参数,字典参数,匹配 map?ids[a]=123&ids[b]=456&ids[c]=789
r.GET("/map", func(c *gin.Context) {
c.JSON(http.StatusOK, c.QueryMap("ids"))
})
路由分组
v1Group := r.Group("/v1")
{
v1Group.GET("/user", func(c *gin.Context) {
c.String(200, "这是v1版本/v1/user")
})
}
v2Group := r.Group("/v2")
{
v2Group.GET("/user", func(c *gin.Context) {
c.String(200, "这是v2版本/v2/user")
})
}
3.输出渲染格式
gin可以很方便的渲染输出数据的格式
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/someString", func(c *gin.Context) {
c.String(http.StatusOK, "string")
})
// gin.H 是 map[string]interface{} 的一种快捷方式
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
// 你也可以使用一个结构体
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "Lena"
msg.Message = "hey"
msg.Number = 123
// 注意由于`json:"user"`的关系 msg.Name 在 JSON 中变成了 "user"
// 将输出:{"user": "Lena", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
r.GET("/someXML", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.Run()
}
4.中间件
gin.Default()gin.Default()DefaultLoggerRecoveryUseHandlerFunc
// 其中func(c *gin.Context){} 就是一个HandlerFunc类型函数
r.GET("/", func(c *gin.Context) {
c.String(200, "hello")
})
我们现在来尝试如何使用中间件:
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个不包含中间件的路由器实例
r := gin.New()
// 全局使用中间件
// 使用gin自带的 Logger 日志中间件
r.Use(gin.Logger())
// 使用gin自带的 Recovery 中间件从任何 panic 恢复,如果出现 panic,它会写一个 500 错误。
r.Use(gin.Recovery())
// 以上代码相当于 r := gin.Default()
// 添加自定义的全局中间件
r.Use(middleware.Cors())
// 单个路由添加中间件,可以添加任意多个
r.GET("/path", middleware.JWT())
// 路由组中添加中间件,中间件只在该路由组中产生作用
// user := r.Group("/user", middleware.AuthRequired())
user := r.Group("/user")
user.Use(middleware.AuthRequired())
{
user.POST("/login", Login)
}
r.Run()
}
requestTime
package main
import (
"fmt"
"time"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(requestTime())
r.GET("/", func(c *gin.Context) {
fmt.Println("我到这里了 hello")
c.String(200, "hello")
})
r.Run()
}
func requestTime() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now() // 记录开始时间
fmt.Println("这里被我拦截住了")
fmt.Println("因为调用了c.Next() 我现在要走了")
c.Next() // 立即调用下一个HandlerFunc(会产生调用耗时)
fmt.Println("我又回来了")
fmt.Println(time.Since(start)) // 打印本次请求处理时间差
}
}
c.Next()c.Next()
c.Next()c.Abort()c.Abort()
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(authority())
r.GET("/path/:name", func(c *gin.Context) {
fmt.Println("欢迎")
c.String(http.StatusOK, "Hello %s", c.Param("name"))
})
r.Run()
}
func authority() gin.HandlerFunc {
return func(c *gin.Context) {
fmt.Println("开始-权限控制")
isAdmin := c.Param("name") == "admin"
if isAdmin {
c.Next()
} else {
c.Abort() //不会再执行接下去的HandlerFunc了
c.String(http.StatusOK, "对不起,您不是管理员")
}
fmt.Println("结束-权限控制")
}
}
情况①:
情况②:
c.Abort()
c.Next()c.Next()c.Abort()
c.Set(key, value)c.Get(key)
package main
import (
"fmt"
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.Use(authority())
r.GET("/", func(c *gin.Context) {
value, ok := c.Get("key")
if ok {
fmt.Println(value)
}
c.String(http.StatusOK, "Hello")
})
r.Run()
}
func authority() gin.HandlerFunc {
return func(c *gin.Context) {
c.Set("key", "你好")
c.Next()
}
}
请求结果:
关于更多gin的信息可以参考文档:https://learnku.com/docs/gin-gonic/2019