目录
1.Web
Web就是一个请求一个响应
package main
import (
"fmt"
"net/http"
)
func sayHello(w http.ResponseWriter, r *http.Request) { // 定义一个
fmt.Fprintln(w, "hello world!")
}
func main() {
http.HandleFunc("/hello", sayHello) //浏览器访问/hello,将会转到sayHello去处理
http.ListenAndServe(":8888", nil) // 监听端口并处理请求
}
2.使用Gin框架构建一个简单的web服务器
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func sayHello(c *gin.Context) { //不同于net/http库的路由函数,gin.Context封装了request和response
c.String(http.StatusOK, "Hello World!")
}
func main() {
r := gin.Default() //1.指定默认路由
r.GET("/hello", sayHello) //2.浏览器访问/hello,将由sayHello函数处理
r.Run(":8080") //3.监听端口
}
3.Gin返回jSON
3.1使用map
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
data := map[string]interface{}{
"message": "seesunman",
}
r.GET("json", func(c *gin.Context) {
c.JSON(http.StatusOK, data)
})
r.Run(":8080")
}
3.2使用map封装的gin.H
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
data := gin.H{
"message": "seesunman",
"age": "18",
}
r.GET("json", func(c *gin.Context) {
c.JSON(http.StatusOK, data)
})
r.Run(":8080")
}
3.3结构体
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type User struct {
Name string
Age int
}
func main() {
r := gin.Default()
data := User{
Name: "seesunman",
Age: 21,
}
r.GET("json", func(c *gin.Context) {
c.JSON(http.StatusOK, data)
})
r.Run(":8080")
}
4.获取参数
4.1获取querystring的参数
有Query、DefaultQuery、GetQuery
4.1.1Query
Query返回key对应的value,如果key不存在返回""
""不等同于nil!!!
// Query returns the keyed url query value if it exists,
// otherwise it returns an empty string `("")`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
// GET /path?id=1234&name=Manu&value=
// c.Query("id") == "1234"
// c.Query("name") == "Manu"
// c.Query("value") == ""
// c.Query("wtf") == ""
func (c *Context) Query(key string) string {
value, _ := c.GetQuery(key)
return value
}
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLFiles("login.html")
r.GET("/hello", func(c *gin.Context) {
username := c.Query("username")
password := c.Query("password")
c.JSON(http.StatusOK, gin.H{
"Username": username,
"Password": password,
})
})
r.Run(":8080")
}
4.1.2DefaultQuery
key存在时返回对应的value,不存在返回默认的value
// DefaultQuery returns the keyed url query value if it exists,
// otherwise it returns the specified defaultValue string.
// See: Query() and GetQuery() for further information.
// GET /?name=Manu&lastname=
// c.DefaultQuery("name", "unknown") == "Manu"
// c.DefaultQuery("id", "none") == "none"
// c.DefaultQuery("lastname", "none") == ""
func (c *Context) DefaultQuery(key, defaultValue string) string {
if value, ok := c.GetQuery(key); ok {
return value
}
return defaultValue
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLFiles("login.html")
r.GET("/hello", func(c *gin.Context) {
// username := c.Query("username")
username := c.DefaultQuery("xxx", "梁朝伟")
password := c.Query("password")
c.JSON(http.StatusOK, gin.H{
"Username": username,
"Password": password,
})
})
r.Run(":8080")
}
4.1.3GetQuery
返回两个元素,第一个元素和Query返回的一样,第二个元素返回真假
查找指定参数时返回(value,true)
未查找到指定参数时返回("", false)
// GetQuery is like Query(), it returns the keyed url query value
// if it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns `("", false)`.
// It is shortcut for `c.Request.URL.Query().Get(key)`
// GET /?name=Manu&lastname=
// ("Manu", true) == c.GetQuery("name")
// ("", false) == c.GetQuery("id")
// ("", true) == c.GetQuery("lastname")
func (c *Context) GetQuery(key string) (string, bool) {
if values, ok := c.GetQueryArray(key); ok {
return values[0], ok
}
return "", false
}
4.2获取form表单的数据
4.2.1PostForm
表单中如果有key,则返回对应的value;没有key则返回空字符串""
// PostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns an empty string `("")`.
func (c *Context) PostForm(key string) string {
value, _ := c.GetPostForm(key)
return value
}
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/form", func(c *gin.Context) {
username := c.PostForm("username")
password := c.PostForm("password")
c.JSON(http.StatusOK, gin.H{
"Username": username,
"Password": password,
})
})
r.Run(":8080")
}
4.2.2DefaultPostForm
有key返回value,没key返回默认value
// DefaultPostForm returns the specified key from a POST urlencoded form or multipart form
// when it exists, otherwise it returns the specified defaultValue string.
// See: PostForm() and GetPostForm() for further information.
func (c *Context) DefaultPostForm(key, defaultValue string) string {
if value, ok := c.GetPostForm(key); ok {
return value
}
return defaultValue
}
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.POST("/form", func(c *gin.Context) {
username := c.DefaultPostForm("username", "seesunman")
password := c.PostForm("password")
c.JSON(http.StatusOK, gin.H{
"Username": username,
"Password": password,
})
})
r.Run(":8080")
}
4.2.3GetPostForm
和PostForm一致,但多了一个返回值表示true/false
// GetPostForm is like PostForm(key). It returns the specified key from a POST urlencoded
// form or multipart form when it exists `(value, true)` (even when the value is an empty string),
// otherwise it returns ("", false).
// For example, during a PATCH request to update the user's email:
// email=mail@example.com --> ("mail@example.com", true) := GetPostForm("email") // set email to "mail@example.com"
// email= --> ("", true) := GetPostForm("email") // set email to ""
// --> ("", false) := GetPostForm("email") // do nothing with email
func (c *Context) GetPostForm(key string) (string, bool) {
if values, ok := c.GetPostFormArray(key); ok {
return values[0], ok
}
return "", false
}
4.3获取路径参数
在/后的路径前加:,来设置路径变量。
使用c.param("路径变量")获取路径变量
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/user/:name/:age", func(c *gin.Context) {
name := c.Param("name")
age := c.Param("age")
c.JSON(http.StatusOK, gin.H{
"name": name,
"age": age,
})
})
r.Run(":8080")
}
5.绑定参数
5.1结构体手动参数绑定
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type User struct {
Name string
Password string
}
func main() {
r := gin.Default()
r.GET("/hello", func(c *gin.Context) {
username := c.Query("username")
password := c.Query("password")
u := User{
Name: username,
Password: password,
}
c.JSON(http.StatusOK, u)
})
r.Run(":8080")
}
5.2ShouldBind
支持处理的数据有:querystring、form表单、json格式
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type UserInfo struct {
Username string `form:"username" json:"username"`
Password string `form:"password" json:"password"`
}
func main() {
c := gin.Default()
c.POST("/hello", func(c *gin.Context) {
var u UserInfo
c.ShouldBind(&u)
c.JSON(http.StatusOK, u)
})
c.Run(":8080")
}
6.上传文件
前端代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="f1">
<input type="submit" value="上传">
</form>
</body>
</html>
gin框架代码
package main
import (
"net/http"
"path"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.LoadHTMLGlob("lesson15/*")
r.GET("/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "index.html", nil)
})
r.POST("/upload", func(c *gin.Context) {
// 从请求中读取文件
f, err := c.FormFile("f1") // 从请求中获取携带的参数一样
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{
"err": err.Error(),
})
} else {
// 将读取到的文件保存到本地(服务端)
// dst := fmt.Sprintf("./%s", f.Filename)
dst := path.Join("./lesson15/", f.Filename)
c.SaveUploadedFile(f, dst)
c.JSON(http.StatusOK, gin.H{
"status": "ok",
})
}
})
r.Run(":8080")
}
7.请求重定向和请求转发
请求重定向
r.GET("/index", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://seesunman.top")
})
请求转发
r.GET("/a", func(c *gin.Context) {
// 跳转到 /b 对应的路由处理函数
c.Request.URL.Path = "/b" // 把请求的URI修改
r.HandleContext(c) // 继续后续的处理
})
r.GET("/b", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "b",
})
})
8.路由和路由组
8.1路由
路由:解析URL,调用对应的控制器(函数)
4种常用请求
r.GET("/index", func(ctx *gin.Context) {
ctx.JSON(http.StatusOK, gin.H{
"method": "GET",
})
})
r.POST("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"method": "POST",
})
})
// PUT用于更新
r.PUT("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"method": "PUT",
})
})
// DELETE用于删除API
r.DELETE("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"method": "DELETE",
})
})
Any:可以接受任意请求
// Any:请求方法大杂烩
r.Any("/user", func(c *gin.Context) {
switch c.Request.Method {
case http.MethodGet:
c.JSON(http.StatusOK, gin.H{"method": "GET"})
case http.MethodPost:
c.JSON(http.StatusOK, gin.H{"method": "POST"})
// ...
}
})
NoRouter:专门处理不能处理的路由
r.NoRoute(func(c *gin.Context) {
c.JSON(http.StatusNotFound, gin.H{"msg": "not found"})
})
8.2路由器组
// 路由组的组
// 把公用的前缀提取出来,创建一个路由组
videoGroup := r.Group("/video")
{
videoGroup.GET("/index", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "index"})
})
videoGroup.GET("/xx", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "xx"})
})
videoGroup.GET("/oo", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "oo"})
})
}
// 可以访问/video/index、/video/xx、/video/oo
9中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
9.1定义中间件
Gin的中间件必须是一个gin.HandleFunc类型
func m1(c *gin.Context) {
//定义一个计时的中间件
start := time.Now()
c.Next()
cost := time.Since(start)
fmt.Printf("cost: %v\n", cost)
}
c.Next() // 调用该请求的剩余处理程序
c.Abort() // 不调用该请求的剩余处理程序
c.Set() // 在上下文中设置值,方便Get获取
c.Get //获取上下文中的值
9.2注册中间件
定义有如下函数indexHandler和中间件m1
func indexHandler(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"msg": "index"})
}
func m1(c *gin.Context) {
//定义一个计时的中间件
start := time.Now()
c.Next()
cost := time.Since(start)
fmt.Printf("cost: %v\n", cost)
}
9.2.1为全局路由注册
func main() {
r := gin.Default()
r.Use(m1) //为全局路由注册中间件
r.GET("/index", indexHandler)
r.Run(":8090")
}
9.2.2为单个路由注册中间件
func main() {
r := gin.Default()
r.GET("/index", m1, indexHandler)
r.Run(":8090")
}
9.2.3为路由器组注册中间件
// 写法1
indexGroup := r.Group("/index", m1)
{
indexGroup.GET("/xx", indexHandler)
}
// 写法2
index2Group := r.Group("/index")
{
index2Group.Use(m1)
index2Group.GET("/xx2", indexHandler)
}
9.3中间件注意事项
· gin.Default()默认使用了Logger和Recovery中间件。
// Default returns an Engine instance with the Logger and Recovery middleware already attached.
func Default() *Engine {
debugPrintWARNINGDefault()
engine := New()
engine.Use(Logger(), Recovery())
return engine
}
· gin中间件使用goroutine
当在中间件或者hangler中启动新的goroutine时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy)
go funcXX(c.Copy())