import (
"github.com/gin-gonic/gin"
"net/http"
)
/**
RESTful API开发实例
*/
func main() {
engine := gin.Default()
engine.GET("/user", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"message": "GET",
})
})
engine.POST("/user", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"message": "POST",
})
})
engine.PUT("/user", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"message": "PUT",
})
})
engine.DELETE("/user", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"message": "DELETE",
})
})
engine.Run(":8088")
}
【1】gin.H 是map[string]interface{}的缩写
【2】使用结构体时注意只序列化首字母大写的字段
【3】使用结构体可以通过tag的方式自定义返回的数据名称和形式
func main() {
engine := gin.Default()
engine.GET("/json1", func(context *gin.Context) {
//手动拼接JSON串
context.JSON(http.StatusOK, gin.H{"name": "张三", "age": 24})
})
engine.GET("/json2", func(context *gin.Context) {
//使用结构体方式
context.JSON(http.StatusOK, Student{"Pete", 12})
})
engine.Run("127.0.0.1:8088")
}
type Student struct {
Name string `json:"name"`
Age int `json:"age"`
}
【1】context.DefaultQuery(“name”, “默认值”) 当未获取到对应的参数时设置默认值
【2】context.Query(“age”)获取请求参数,如果没有则空
func main() {
engine := gin.Default()
engine.GET("/search", func(context *gin.Context) {
query := context.DefaultQuery("name", "默认值")
age := context.Query("age")
fmt.Printf("参数name: %v 参数age: %v ", query, age)
//接收到请求后再响应结果
context.JSON(http.StatusOK, gin.H{"status": "OK", "msg": "message..."})
})
engine.Run(":8088")
}
【1】 context.DefaultPostForm(“name”, “默认值”) 如果未获取到表单参数数据则取默认值
【2】context.PostForm(“age”)如果未获取到表单参数数据则为空
当前端请求的数据通过form表单提交时,例如向/search发送一个POST请求,获取请求数据的方式如下:
engine.POST("/search", func(context *gin.Context) {
name := context.DefaultPostForm("name", "默认值")
age := context.PostForm("age")
fmt.Printf("参数name: %v 参数age: %v ", name, age)
//接收到请求后再响应结果
context.JSON(http.StatusOK, gin.H{"status": "OK", "msg": "message..."})
})
【1】使用GetRawData来获取JSON格式的请求参数数据
【2】后台接收到之后可以通过map或者结构体来进行反序列化接收参数
engine.POST("/formJson", func(c *gin.Context) {
//读取row格式请求体数据
b, _ := c.GetRawData()
// 定义map或结构体
var m map[string]interface{}
// 反序列化
_ = json.Unmarshal(b, &m)
fmt.Println("JSON请求参数", m)
c.JSON(http.StatusOK, m)
})
【1】通过:参数名称匹配路径参数
【2】使用context.Param(“name”)方式获取路径参数值
//获取路径参数
engine.GET("/search/:name/:age", func(context *gin.Context) {
name := context.Param("name")
age:=context.Param("age")
context.JSON(http.StatusOK, gin.H{"name": name, "age": age,"msg":"路径参数测试"})
})
【1】为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。Gin中所提供的ShouldBind()能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
【2】对于需要绑定的字段值,其字段名称需要首字母大写
【3】对于名称不一致的可以通过tag的方式映射
【4】由于结构体类型是值拷贝,所以在作为参数传递并想要拿到结果则需要传递结构体地址
【5】ShouldBind会按照下面的顺序解析请求中的数据完成绑定:
如果是 GET 请求,只使用 Form 绑定引擎(query)。如果是 POST 请求,首先检查 content-type 是否为 JSON 或 XML,然后再使用 Form(form-data)。
type UserInfo struct {
UserName string `form:"userName" json:"userName" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
func main() {
engine := gin.Default()
engine.GET("/login", func(context *gin.Context) {
var userInfo UserInfo
err := context.ShouldBind(&userInfo)
if err == nil {
fmt.Printf("login info:%#v\n", userInfo)
context.JSON(http.StatusOK, gin.H{
"userName": userInfo.UserName,
"password": userInfo.Password,
})
} else {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
engine.POST("/login", func(context *gin.Context) {
var userInfo UserInfo
err := context.ShouldBind(&userInfo)
if err == nil {
fmt.Printf("login info:%#v\n", userInfo)
context.JSON(http.StatusOK, gin.H{
"userName": userInfo.UserName,
"password": userInfo.Password,
})
} else {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
engine.Run(":8088")
}
【1】使用context.FormFile(“file”)获取单个文件,使用context.MultipartForm()…File[“file”]获取上传的多个文件
【2】处理multipart forms提交文件时默认的内存限制是32 MiB,可以通过engine .MaxMultipartMemory = 8 << 20 // 8 MiB 修改
【3】文件的获取在开发过程中注意处理异常
【4】通过Context.SaveUploadedFile(file, dst) 简单方便的保存文件到服务器
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
engine := gin.Default()
//单个文件上传
engine.POST("/upload", func(context *gin.Context) {
file, err := context.FormFile("file")
if err != nil {
context.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(),
})
return
}
dst := fmt.Sprintf("./src/file/%s", file.Filename)
// 上传文件到指定的目录
_ = context.SaveUploadedFile(file, dst)
context.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("'%s' 文件已上传", file.Filename),
})
})
//多文件上传
engine.POST("/mulUpload", func(context *gin.Context) {
// Multipart form
form, _ := context.MultipartForm()
files := form.File["file"]
for _, file := range files {
dst := fmt.Sprintf("./src/file/%s", file.Filename)
// 上传文件到指定的目录
_ = context.SaveUploadedFile(file, dst)
}
context.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d 多文件已上传", len(files)),
})
})
engine.Run(":8088")
}
【1】使用context.Redirect可以实现内部和外部的重定向
【2】使用gin.Default().HandleContext可以实现路由重定向
// HTTP重定向
engine.GET("/test", func(context *gin.Context) {
context.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
})
// 路由重定向
engine.GET("/test", func(context *gin.Context) {
// 指定重定向的URL
context.Request.URL.Path = "/hello"
engine.HandleContext(context)
})
engine.GET("/hello", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{"message": "hello"})
})
【1】普通的路由可以使用engine.GET、engine.POST、engine.PUT、engine.DELETE等方式处理
【2】engine.Any匹配任意请求Method的路由
【3】为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码,我们也可以通过engine.NoRoute自定义内容处理
【4】对于拥有公共前缀的路由,为了书写方便看着清晰,我们可以通过路由组的方式来管理,engine.Group(“/student”)就是定义了一个/student前缀的路由组,下面实例中路由组{}是可选的,可以保留也可以去掉
【5】注意,路由组内是可以继续嵌套路由组的
import (
"github.com/gin-gonic/gin"
"net/http"
)
/**
路由和路由组
*/
func main() {
engine := gin.Default()
//普通路由
engine.GET("/demo", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{"msg": "demo"})
})
//匹配任意请求Method的路由
engine.Any("/any", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{"msg": "any"})
})
//处理未匹配的路由
engine.NoRoute(func(context *gin.Context) {
context.JSON(http.StatusNotFound, gin.H{"msg": "NoRoute"})
})
//路由组
userGroup:=engine.Group("/user")
{
userGroup.GET("/list", func(context *gin.Context) {
context.JSON(http.StatusNotFound, gin.H{"msg": "user.list"})
})
userGroup.POST("/add", func(context *gin.Context) {
context.JSON(http.StatusNotFound, gin.H{"msg": "user.add"})
})
}
stuGroup:=engine.Group("/student")
{
stuGroup.GET("/list", func(context *gin.Context) {
context.JSON(http.StatusNotFound, gin.H{"msg": "student.list"})
})
stuGroup.POST("/add", func(context *gin.Context) {
context.JSON(http.StatusNotFound, gin.H{"msg": "student.add"})
})
}
engine.Run(":8088")
}
【1】Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
【2】Gin中间件需要是gin.HandlerFunc类型,如下图所示负责处理路由的就是这些HandlerFunc,可以看到如下是支持可变参数的,在执行的过程中是依次
【3】在中间件执行的过程中,可以使用Gin框架提供的程序链相关的API完成特定的需求功能;
func (c *Context) Next() : 执行程序链后面的中间件,暂时挂起当前程序
func (c *Context) Abort() : 阻止调用后续的程序链中间件,但不会停止当前处理程序。比如在进行授权认证时,如果认证失败则不进行后续执行。
【4】针对全部的处理,比如日志、安全、权限等等可以使用全局注册的方式,对于特定组或者特定路由的处理可以使用单个配置
【5】当全局注册多个中间件,其执行顺序如下图所示,和Java程序员所熟知的过滤器拦截器都是类似的
【6】对于程序链中在上下文设值和取值可以通过context.Set()和context.get()处理,键一个字符串类型,值是空接口类型
//统计请求耗时
func computeCost() gin.HandlerFunc {
return func(context *gin.Context) {
start := time.Now()
context.Set("message", "Hello")
context.Next()
cost := time.Since(start)
fmt.Printf("处理耗时%d:", cost)
}
}
func main() {
engine := gin.Default()
//全局注册中间件
engine.Use(computeCost())
//下面的第二个参数就是一个中间件,可以传入多个
engine.GET("/demo", func(context *gin.Context) {
value, exists := context.Get("message")
if exists {
context.JSON(http.StatusOK, gin.H{"msg": value})
}
})
engine.Run(":8088")
}