GoWeb
创建一个简单Web程序
package main
import (
"fmt"
"log"
"net/http"
)
// 创建处理器函数
func handler(w http.ResponseWriter, r *http.Request) {
fmt.Fprintln(w, "Hello World", r.URL.Path)
}
func main() {
// 映射请求地址 ,将请求地址和处理器相映射
http.HandleFunc("/", handler)
// 设置监听端口 和处理器
// 第一个参数是端口号,如果不设置,默认是80,
// 第二个参数是 处理器,若参数为nil,服务器将使用默认的多路复用器 DefaultServerMux
err := http.ListenAndServe(":8000", nil)
if err != nil{
log.Fatalln("ListenAndServer", err)
}
}
复制代码
创建Go服务器
Go 提供了一系列用于创建 Web 服务器的标准库,而且通过 Go 创建一个服务器的 步骤非常简单,只要通过 net/http 包调用 ListenAndServe 函数并传入网络地址以及负 责处理请求的处理器( handler )作为参数就可以了。如果网络地址参数为空字符串,那 么服务器默认使用 80 端口进行网络连接;如果处理器参数为 nil,那么服务器将使用默 认的多路复用器 DefaultServeMux,当然,我们也可以通过调用 NewServeMux 函数创 建一个多路复用器。多路复用器接收到用户的请求之后根据请求的 URL 来判断使用哪 个处理器来处理请求,找到后就会重定向到对应的处理器来处理请求
Gin框架快速入门
gin简介
Gin
是一个用Go语言编写的web框架。它是一个类似于martini
但拥有更好性能的API框架, 由于使用了httprouter
, 速度提高了近40倍。
gin安装:
Terminal 输入go get github.com/gin-gonic/gin
进行安装
每次新建项目时:go mod inti projectName
若下载失败参考链接:https://www.cnblogs.com/kevin-yang123/p/14799091.html
第一个gin程序
package main
import (
"github.com/gin-gonic/gin"
)
//创建处理器函数
func sayHello(c *gin.Context){
c.JSON(200,gin.H{ //gin.H 是json字符串
"message": "hello",
})
}
func main() {
// 返回一个默认的路由引擎
r := gin.Default()
// GET: 请求方式; /hello: 请求路径
// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
r.GET("/hello", sayHello )
// 启动http服务,默认在0.0.0:8080启动服务
r.Run(":9090")
}
复制代码
RESTful API风格
REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”
简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
推荐阅读www.ruanyifeng.com/blog/2011/0…
GET
用来获取资源POST
用来新建资源PUT
用来更新资源DELETE
用来删除资源。
只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。
// GET
r.GET("/book", func(context *gin.Context){
context.JSON(http.StatusOK, gin.H{
"method": "GET",
})
})
// POST
r.POST("/book", func(context *gin.Context){
context.JSON(http.StatusOK, gin.H{
"method": "POST",
})
})
//PUT
r.PUT("/book", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"method": "PUT",
})
})
//DElETE
r.DELETE("/book", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"method": "DELETE",
})
})
复制代码
Gin框架返回JSON
方法一:使用map ,gin.H 与其相同
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 创建默认路由
r := gin.Default()
r.GET("/json1", func(context *gin.Context) {
// 方法一 :使用map ,gin.H 与其相同
/*data := map[string]interface{}{
"name" : "LittlePrince",
"message" : "helloworld",
"age" : 16,
}
*/
data1 := gin.H{
"name" : "LittlePrince",
"message" : "helloworld",
"age" : 16,
}
context.JSON(http.StatusOK, data1)
})
r.Run(":9091")
}
复制代码
方法二:结构体 首字母大写,保证JSON可以取到
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
// 创建默认路由
r := gin.Default()
// 方法二: 结构体 首字母大写,保证JSON可以取到 ,可以使用tag对结构体字段进行自定义操作
type msg struct{
Name string `json:"name"`
Message string
Age int
}
r.GET("/json2", func(context *gin.Context) {
data2 := msg{
Name: "LittlePringe",
Message: "hello gin",
Age: 14,
}
context.JSON(http.StatusOK, data2)
})
r.Run(":9091")
}
复制代码
获取参数
获取querystring参数(URL中)
querystring
指的是URL中?
后面携带的参数,例如:/user/search?username=小王子&address=沙河
。 获取请求的querystring参数的方法如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/usr/search", func(context *gin.Context) {
username := context.DefaultQuery("username", "LittlePrince")
address := context.Query("address")
// 输出json结果给调用方
context.JSON(http.StatusOK, gin.H{
"message" : "ok",
"username" : username,
"address" : address,
})
})
r.Run(":9091")
}
复制代码
获取form表单中的参数
当前端请求的数据通过form表单提交时,例如向/user/search
发送一个POST请求,获取请求数据的方式如下:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/usr/search", func(context *gin.Context) {
// DefaultPostForm取不到值时会返回指定的默认值
//username := context.DefaultPostForm("username", "小王子")
username := context.PostForm("username")
address := context.PostForm("address")
context.JSON(http.StatusOK, gin.H{
"message" : "ok",
"username" : username,
"address" : address,
})
})
r.Run(":9091")
}
复制代码
获取JSON的参数
当前端请求的数据通过JSON提交时,例如向/json
发送一个POST请求,则获取请求参数的方式如下:
package main
import (
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.POST("/json3", func(context *gin.Context) {
b, _ := context.GetRawData() // 从context.Request.Body读取请求数据
// 定义map或结构体
var m map[string]interface{}
// 反序列化
_ = json.Unmarshal(b, &m)
context.JSON(http.StatusOK, m)
})
r.Run(":9091")
}
复制代码
获取Path参数
请求的参数通过URL路径传递,例如:/user/search/小王子/沙河
。 获取请求URL路径中的参数的方式如下。
package main
import (
"encoding/json"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("usr/serarch/:username/:address", func(context *gin.Context) {
username := context.Param("username")
address := context.Param("address")
//将结果输出到调用方
context.JSON(http.StatusOK, gin.H{
"message" : "ok",
"username" : username,
"address" : address,
})
})
r.Run(":9091")
}
复制代码
自动参数绑定
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type
识别请求数据类型并利用反射机制自动提取请求中QueryString
、form表单
、JSON
、XML
等参数到结构体中。 下面的示例代码演示了.ShouldBind()
强大的功能,它能够基于请求自动提取JSON
、form表单
和QueryString
类型的数据,并把值绑定到指定的结构体对象。
ShouldBind
会按照下面的顺序解析请求中的数据完成绑定:
- 如果是
GET
请求,只使用Form
绑定引擎(query
)。 - 如果是
POST
请求,首先检查content-type
是否为JSON
或XML
,然后再使用Form
(form-data
)。
实例如下
package main
import (
"encoding/json"
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
type Login struct {
User string `form:"user" json:"user" binding:"required"`
Password string `form:"password" json:"password" binding:"required"`
}
// 绑定Json示例 ({"user": "q1mi", "password": "123456"})
r.POST("/loginJson", func(context *gin.Context) {
var login Login
// 和login绑定
if err := context.ShouldBind(&login); err == nil{
fmt.Printf("login info: %#v \n", login)
context.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
}else {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
// 绑定form表单示例 (user=q1mi&password=123456)
r.POST("/loginForm", func(c *gin.Context) {
var login Login
// ShouldBind()会根据请求的Content-Type自行选择绑定器
if err := c.ShouldBind(&login); err == nil {
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
// 绑定QueryString示例 (/loginQuery?user=q1mi&password=123456)
r.GET("/loginForm", func(c *gin.Context) {
var login Login
// ShouldBind()会根据请求的Content-Type自行选择绑定器
if err := c.ShouldBind(&login); err == nil {
c.JSON(http.StatusOK, gin.H{
"user": login.User,
"password": login.Password,
})
} else {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
}
})
r.Run(":9091")
}
复制代码
重定向
可用与访问资源出错定位到指定页面
http重定向
HTTP 重定向很容易。 内部、外部重定向均支持。
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/baidu", func(context *gin.Context) {
// http 重定向
context.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})
r.Run(":8081")
}
复制代码
路由重定向
路由重定向,使用HandleContext
:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/sogo", func(context *gin.Context) {
//路由重定向
context.Request.URL.Path = "/baidu"
r.HandleContext(context)
})
r.GET("/test", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"user": "login.User",
"password": "login.Password",
})
})
r.Run(":8081")
}
复制代码
Gin路由
普通路由
上述已经使用过的普通路由
r.GET("/index", func(c *gin.Context) {...})
r.GET("/login", func(c *gin.Context) {...})
r.POST("/login", func(c *gin.Context) {...})
复制代码
此外,还有一个可以匹配所有请求方法的Any
方法如下:
r.Any("/test", func(c *gin.Context) {...})
复制代码
为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码,下面的代码为没有匹配到路由的请求都返回views/404.html
页面。
r.NoRoute(func(c *gin.Context) {
c.HTML(http.StatusNotFound, "views/404.html", nil)
})
复制代码
路由组
我们可以将拥有共同URL前缀的路由划分为一个路由组。习惯性一对{}
包裹同组的路由,这只是为了看着清晰,你用不用{}
包裹功能上没什么区别
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
// 创建一个路由组
userGroup := r.Group("/user")
{
// 路由组中 第一个路由
userGroup.GET("/index", func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"time" : "teime",
})
})
// 路由组中 第二个路由
userGroup.POST("/login" , func(context *gin.Context) {
context.JSON(http.StatusOK, gin.H{
"test" : "test",
})
})
}
r.Run(":8081")
}
复制代码
路由组也是支持嵌套的,例如:
shopGroup := r.Group("/shop")
{
shopGroup.GET("/index", func(c *gin.Context) {...})
shopGroup.GET("/cart", func(c *gin.Context) {...})
shopGroup.POST("/checkout", func(c *gin.Context) {...})
// 嵌套路由组
xx := shopGroup.Group("xx")
xx.GET("/oo", func(c *gin.Context) {...})
}
复制代码
通常我们将路由分组用在划分业务逻辑或划分API版本时。
中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
一个简单的中间件Demo
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
)
// 中间件ue是一个函数
func midddleware(c *gin.Context) {
fmt.Println("middleware....")
}
func task(c *gin.Context) {
fmt.Println("task.....")
c.JSON(http.StatusOK, gin.H{
"task":"sfasfsa",
})
}
func main() {
r := gin.Default()
// 中间件在task执行之前执行
r.GET("/index", midddleware, task)
r.Run(":8081")
}
复制代码
使用中间件统计处理请求时间
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"net/http"
"time"
)
// 统计耗时中间件
func midddleware(c *gin.Context) {
fmt.Println("middleware....")
start := time.Now() //获取现在的时间
c.Set("name", "LittlePrince") //通过c.set在请求上下文中设置值,后续的处理函数能够得到值
c.Next() // 调用后续的处理程序
//c.Abort() // 组织后续的处理程序
// 计算耗时
cost := time.Since(start)
fmt.Printf("cost:%v \n",cost)
fmt.Println("middleware out")
}
func task(c *gin.Context) {
fmt.Println("task.....")
c.JSON(http.StatusOK, gin.H{
"task":"sfasfsa",
})
}
func main() {
r := gin.Default()
r.GET("/index", midddleware, task)
r.Run(":8081")
}
复制代码
给一组路由添加中间件
在gin框架中,我们可以为每个路由添加任意数量的中间件。
为全局路由注册中间件
func main() {
// 新建一个没有任何默认中间件的路由
r := gin.Default
// 注册一个全局中间件
r.Use(midddleware)
r.GET("/test", func(c *gin.Context) {
name := c.MustGet("name").(string) // 从上下文取值
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"message": "Hello world!",
})
})
r.Run()
}
复制代码
为某个路由注册中间件
见上面的案例使用中间件统计处理请求时间
为路由组注册中间件
写法1:
shopGroup := r.Group("/shop", StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
复制代码
写法2:
shopGroup := r.Group("/shop")
shopGroup.Use(StatCost())
{
shopGroup.GET("/index", func(c *gin.Context) {...})
...
}
复制代码
实际使用中使用闭包处理
// 实际使用中间件时,使用闭包包装一层
func autMiddleware(doCheck bool) gin.HandlerFunc {
return func(c *gin.Context) {
if doCheck{
// 存放具体逻辑
// 是否登录判断
// if 是用户
// c.next()
// else
// c.abort()
}else {
c.Next()
}
}
}
复制代码
中间件注意事项
gin默认中间件
gin.Default()
默认使用了Logger
和Recovery
中间件,其中:
Logger
中间件将日志写入gin.DefaultWriter
,即使配置了GIN_MODE=release
。Recovery
中间件会recover任何panic
。如果有panic的话,会写入500响应码。
如果不想使用上面两个默认的中间件,可以使用gin.New()
新建一个没有任何默认中间件的路由。
gin中间件中使用goroutine
当在中间件或handler
中启动新的goroutine
时,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy()
)。
近期评论