网站建设公司企业文化seo教程网站优化
文章目录
- 安装
- URL和路由分组
- 2. 带参数的url
- 3. 获取路由分组的参数
- 获取参数
- 1. 获取get参数
- 2. 获取post参数
- 3. get、post混合
- JSON 、 ProtoBuf渲染
- 1. 输出json和protobuf
- 2. PureJSON
- 表单验证
- 1. 表单的基本验证
- 中间件和next函数
- 1. 无中间件启动
- 2. 使用中间件
- 3. 自定义组件
- 设置静态文件路径和HTML文件
- 1. 设置静态文件路径
- 2. index.html内容
- 3. templates/posts/index.tmpl
- 4. templates/users/index.tmpl
- 优雅重启或停止
- gorm
- 1. 什么是orm
- 2. 常用orm
- 3. orm的优缺点
- 4. 如何正确看待orm和sql之间的关系
官方手册
安装
go get -u github.com/gin-gonic/gin
- 代码
package mainimport "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() // listen and serve on 0.0.0.0:8080
}
- 使用get、post、put等http方法
func main() {// 使⽤默认中间件创建⼀个gin路由器// logger and recovery (crash-free) 中间件router := gin.Default()router.GET("/someGet", getting)router.POST("/somePost", posting)router.PUT("/somePut", putting)router.DELETE("/someDelete", deleting)router.PATCH("/somePatch", patching)router.HEAD("/someHead", head)router.OPTIONS("/someOptions", options)// 默认启动的是 8080端⼝,也可以⾃⼰定义启动端⼝router.Run()// router.Run(":3000") for a hard coded port
}
URL和路由分组
- 路由分组
func main() {router := gin.Default()// Simple group: v1v1 := router.Group("/v1"){v1.POST("/login", loginEndpoint)v1.POST("/submit", submitEndpoint)v1.POST("/read", readEndpoint)}// Simple group: v2v2 := router.Group("/v2"){v2.POST("/login", loginEndpoint)v2.POST("/submit", submitEndpoint)v2.POST("/read", readEndpoint)}router.Run(":8082")
}
2. 带参数的url
package mainimport ("github.com/gin-gonic/gin""net/http")func main() {r := gin.Default()r.GET("/ping", func(c *gin.Context) {c.JSON(200, gin.H{"message": "pong",})})r.GET("/user/:name/:action/", func(c *gin.Context) {name := c.Param("name")action := c.Param("action")c.String(http.StatusOK, "%s is %s", name, action)})r.GET("/user/:name/*action", func(c *gin.Context) {name := c.Param("name")action := c.Param("action")c.String(http.StatusOK, "%s is %s", name, action)})r.Run(":8082")
}
3. 获取路由分组的参数
package mainimport "github.com/gin-gonic/gin"type Person struct {ID string `uri:"id" binding:"required,uuid"`Name string `uri:"name" binding:"required"`}func main() {route := gin.Default()route.GET("/:name/:id", func(c *gin.Context) {var person Personif err := c.ShouldBindUri(&person); err != nil {c.JSON(400, gin.H{"msg": err})return}c.JSON(200, gin.H{"name": person.Name, "uuid": person.ID})})route.Run(":8088")
}
获取参数
1. 获取get参数
func main() {router := gin.Default()// 匹配的url格式: /welcome?firstname=Jane&lastname=Doerouter.GET("/welcome", func(c *gin.Context) {firstname := c.DefaultQuery("firstname", "Guest")lastname := c.Query("lastname") // 是 c.Request.URL.Query().Get("lastnamec.String(http.StatusOK, "Hello %s %s", firstname, lastname)})router.Run(":8080")
}
2. 获取post参数
func main() {router := gin.Default()router.POST("/form_post", func(c *gin.Context) {message := c.PostForm("message")nick := c.DefaultPostForm("nick", "anonymous") // 此⽅法可以设置默认值c.JSON(200, gin.H{"status": "posted","message": message,"nick": nick,})})router.Run(":8080")
}
3. get、post混合
POST /post?id=1234&page=1 HTTP/1.1
Content-Type: application/x-www-form-urlencoded
name=manu&message=this_is_greatfunc main() {router := gin.Default()router.POST("/post", func(c *gin.Context) {id := c.Query("id")page := c.DefaultQuery("page", "0")name := c.PostForm("name")message := c.PostForm("message")fmt.Printf("id: %s; page: %s; name: %s; message: %s", id, page, name, mes})router.Run(":8080")
}
JSON 、 ProtoBuf渲染
1. 输出json和protobuf
新建user.proto文件
syntax = "proto3";
option go_package = ".;proto";
message Teacher {string name = 1;repeated string course = 2;
}
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"start/gin_t/proto"
)
func main() {r := gin.Default()// gin.H is a shortcut for 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) {// You also can use a structvar msg struct {Name string `json:"user"`Message stringNumber int}msg.Name = "Lena"msg.Message = "hey"msg.Number = 123// Note that msg.Name becomes "user" in the JSON// Will output : {"user": "Lena", "Message": "hey", "Number": 123}c.JSON(http.StatusOK, msg)})r.GET("/someProtoBuf", func(c *gin.Context) {courses := []string{"python", "django", "go"}// The specific definition of protobuf is written in the testdata/protoexName: "bobby",Course: courses,}// Note that data becomes binary data in the response// Will output protoexample.Test protobuf serialized datac.ProtoBuf(http.StatusOK, data)})// Listen and serve on 0.0.0.0:8080r.Run(":8083")
}
2. PureJSON
通常情况下,JSON会将特殊的HTML字符替换为对应的unicode字符,比如 < 替换为 \u003c ,如果想原样输出html,则使用PureJSON
func main() {r := gin.Default()// Serves unicode entitiesr.GET("/json", func(c *gin.Context) {c.JSON(200, gin.H{"html": "<b>Hello, world!</b>",})})// Serves literal charactersr.GET("/purejson", func(c *gin.Context) {c.PureJSON(200, gin.H{"html": "<b>Hello, world!</b>",})})// listen and serve on 0.0.0.0:8080r.Run(":8080")
}
表单验证
1. 表单的基本验证
若要将请求主体绑定到结构体中,请使用模型绑定,目前支持JSON、XML、YAML和标准表单值(foo=bar&boo=baz)的绑定。 Gin使用 go-playground/validator 验证参数
需要在绑定的字段上设置tag,比如,绑定格式为json,需要这样设置 json:“fieldname” 。
此外,Gin还提供了两套绑定方法:
- Must bind
Methods - Bind , BindJSON , BindXML , BindQuery , BindYAML Behavior - 这些方法底层使用 MustBindWith ,如果存在绑定错误,请求将被以下指令中止 c. AbortWithError(400,err).SetType(ErrorTypeBind) ,响应状态代码会被设置为400,请求头 Content-Type 被设置为 text/plain; charset=utf-8 。注意,如果你试图在此之后设置响应代码,将会发出一个警告 [GIN-debug] [WARNING] Headers were already written. Wanted to override status code 400 with 422 ,如果你希望更好地控制行为,请使用 ShouldBind 相关的方法 - Should bind
Methods - ShouldBind , ShouldBindJSON , ShouldBindXML , ShouldBindQuery , ShouldBindYAML
Behavior - 这些方法底层使用 ShouldBindWith ,如果存在绑定错误,则返回错误,开发人员可以正确处理请求和错误。
当我们使用绑定方法时,Gin会根据Content-Type推断出使用哪种绑定器,如果你确定你绑定的是什么,你可以使用 MustBindWith 或者 BindingWith 。你还可以给字段指定特定规则的修饰符,如果一个字段用 binding:“required” 修饰,并且在绑定时该字段的值为空,那么将返回一个错误。
// 绑定为json
type Login struct {User string `form:"user" json:"user" xml:"user" binding:"required"`Password string `form:"password" json:"password" xml:"password" binding:"requ`
}type SignUpParam struct {Age uint8 `json:"age" binding:"gte=1,lte=130"`Name string `json:"name" binding:"required"`Email string `json:"email" binding:"required,email"`Password string `json:"password" binding:"required"`RePassword string `json:"re_password" binding:"required,eqfield=Password"`
}
func main() {router := gin.Default()// Example for binding JSON ({"user": "manu", "password": "123"})router.POST("/loginJSON", func(c *gin.Context) {var json Loginif err := c.ShouldBindJSON(&json); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if json.User != "manu" || json.Password != "123" {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})// Example for binding a HTML form (user=manu&password=123)router.POST("/loginForm", func(c *gin.Context) {var form Login// This will infer what binder to use depending on the content-type headeif err := c.ShouldBind(&form); err != nil {c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})return}if form.User != "manu" || form.Password != "123" {c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"})return}c.JSON(http.StatusOK, gin.H{"status": "you are logged in"})})r.POST("/signup", func(c *gin.Context) {var u SignUpParamif err := c.ShouldBind(&u); err != nil {c.JSON(http.StatusOK, gin.H{"msg": err.Error(),})return}// 保存⼊库等业务逻辑代码...c.JSON(http.StatusOK, "success")})// Listen and serve on 0.0.0.0:8080router.Run(":8080")
}
中间件和next函数
1. 无中间件启动
#使⽤
r := gin.New()
#替代
// 默认启动⽅式,包含 Logger、Recovery 中间件
r := gin.Default()
2. 使用中间件
func main() {
// 创建⼀个不包含中间件的路由器r := gin.New()// 全局中间件// 使⽤ Logger 中间件r.Use(gin.Logger())// 使⽤ Recovery 中间件r.Use(gin.Recovery())// 路由添加中间件,可以添加任意多个r.GET("/benchmark", MyBenchLogger(), benchEndpoint)// 路由组中添加中间件// authorized := r.Group("/", AuthRequired())// exactly the same as:authorized := r.Group("/")// per group middleware! in this case we use the custom created// AuthRequired() middleware just in the "authorized" group.authorized.Use(AuthRequired()){authorized.POST("/login", loginEndpoint)authorized.POST("/submit", submitEndpoint)authorized.POST("/read", readEndpoint)// nested grouptesting := authorized.Group("testing")testing.GET("/analytics", analyticsEndpoint)}// Listen and serve on 0.0.0.0:8080r.Run(":8080")
}
3. 自定义组件
func Logger() gin.HandlerFunc {return func(c *gin.Context) {t := time.Now()// Set example variablec.Set("example", "12345")// before requestc.Next()// after requestlatency := time.Since(t)log.Print(latency)// access the status we are sendingstatus := c.Writer.Status()log.Println(status)}}func main() {r := gin.New()r.Use(Logger())r.GET("/test", func(c *gin.Context) {example := c.MustGet("example").(string)// it would print: "12345"log.Println(example)})// Listen and serve on 0.0.0.0:8080r.Run(":8080")
}
设置静态文件路径和HTML文件
1. 设置静态文件路径
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {// 创建⼀个默认的路由引擎r := gin.Default()// 配置模板r.LoadHTMLGlob("templates/**/*")//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html// 配置静态⽂件夹路径 第⼀个参数是api,第⼆个是⽂件夹路径r.StaticFS("/static", http.Dir("./static"))// GET:请求⽅式;/hello:请求的路径// 当客户端以GET⽅法请求/hello路径时,会执⾏后⾯的匿名函数r.GET("/posts/index", func(c *gin.Context) {// c.JSON:返回JSON格式的数据c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{"title": "posts/index",})})r.GET("gets/login", func(c *gin.Context) {c.HTML(http.StatusOK, "posts/login.tmpl", gin.H{"title": "gets/login",})})// 启动HTTP服务,默认在0.0.0.0:8080启动服务r.Run()
}
2. index.html内容
<html><h1>{{ .title }}</h1>
</html>
3. templates/posts/index.tmpl
{{ define "posts/index.tmpl" }}<html><h1>{{ .title }}</h1><p>Using posts/index.tmpl</p></html>
{{ end }}
4. templates/users/index.tmpl
{{ define "users/index.tmpl" }}
<html><h1>{{ .title }}
</h1><p>Using users/index.tmpl</p>
</html>
{{ end }}
优雅重启或停止
package main
import ("context""log""net/http""os""os/signal""syscall""time""github.com/gin-gonic/gin"
)
func main() {router := gin.Default()router.GET("/", func(c *gin.Context) {time.Sleep(5 * time.Second)c.String(http.StatusOK, "Welcome Gin Server")})
srv := &http.Server{Addr: ":8080",Handler: router,
}
go func() {// service connectionsif err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosedlog.Fatalf("listen: %s\n", err)}
}()// Wait for interrupt signal to gracefully shutdown the server with// a timeout of 5 seconds.quit := make(chan os.Signal)// kill (no param) default send syscanll.SIGTERM// kill -2 is syscall.SIGINT// kill -9 is syscall. SIGKILL but can"t be catch, so don't need add itsignal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)<-quitlog.Println("Shutdown Server ...")ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)defer cancel()if err := srv.Shutdown(ctx); err != nil {log.Fatal("Server Shutdown:", err)
select {case <-ctx.Done():log.Println("timeout of 5 seconds.")}log.Println("Server exiting")
}
gorm
1. 什么是orm
ORM全称是:Object Relational Mapping(对象关系映射),其主要作用是在编程中,把面向对象的概念跟数据库中表的概念对应起来。举例来说就是,我定义一个对象,那就对应着一张表,这个对象的实例,就对应着表中的一条记录。
对于数据来说,最重要最常用的是表:表中有列, orm就是将一张表映射成一个类,表中的列映射成类中的一个类。java 、python,但是针对go语言而言,struct,就是列如何映射,是因为列可以映射成struct中的类型,int->int,但是有另一个问题? 就是数据库中的列具备很好的描述性,但是struct有tag。执行sql, 需要我们有足够的sql语句基础、需要我们懂得不同的数据的sql
2. 常用orm
个人而言,不用太去纠结应该选择哪一个orm框架,但是实际上你用熟悉了一个,其他的orm迁移成本很低,我们选个一个star数量最高的一定不会有错,这些差异也不会很大
sql语言远比orm重要的多
https://github.com/go-gorm/gorm
https://github.com/facebook/ent
https://github.com/jmoiron/sqlx
https://gitea.com/xorm/xorm/src/branch/master/README_CN.md
https://github.com/didi/gendry/blob/master/translation/zhcn/README.md
3. orm的优缺点
优点:
- 提高了开发效率。
- 屏蔽sql细节。可以自动对实体Entity对象与数据库中的Table进行字段与属性的映射;不用直接SQL编码
- 屏蔽各种数据库之间的差异
缺点: - orm会牺牲程序的执行效率和会固定思维模式
- 太过依赖orm会导致sql理解不够
- 对于固定的orm依赖过重,导致切换到其他的orm代价高
4. 如何正确看待orm和sql之间的关系
- sql为主,orm为辅
- orm主要目的是为了增加代码可维护性和开发效率
一定要学好:
- group by
- 子查询
- having子句