gin框架的安装使用(路由冲突conflict、URL处理规则、请求参数以及参数绑定)

gin框架的安装使用(路由冲突conflict、URL处理规则、请求参数以及参数绑定),第1张

一、gin入门 1、创建go module项目步骤

1、file——》go modeles——创建

2、设置代理:

setting——》go Modules

GOPROXY=https://mirrors.aliyun.com/goproxy/,direct;

3、在go.mod中引入gin

require github.com/gin-gonic/gin v1.6.3/

如果没有安装gin,在引入gin后使用go mod tidy就会自动下载(或者直接下载)
go get -u github.com/gin-gonic/gin

4、创建main.go

package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	engine := gin.Default()

	engine.GET("/helloworld", func(c *gin.Context){
		c.String(http.StatusOK, "hello world")
	})

	//返回JSON格式的数据
	engine.GET("/json", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"name": "admin",
			"age": 18,
		})
	})

	//返回xml格式的数据
	engine.GET("/xml", func(c *gin.Context) {
		c.XML(http.StatusOK, gin.H{
			"name": "zhangsan",
			"age": 19,
		})
	})
	engine.Run()
}

5、运行

在Terminal中输入go run main.go,然后浏览器搜索http://127.0.0.1:8080/helloworld

2、静态文件
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	engine := gin.Default()
	//加载templates下的模板文件
	engine.LoadHTMLGlob("templates/*")
	engine.Static("/static", "./static")

	engine.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK, "index.html", gin.H{
			"name": "admin",
			"age":18,
			"users": []string{"张三","李四","王二"},
		})
	})

	engine.Run()
}


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
    <link rel="stylesheet" href="/static/css/index.css">
head>
<body>
    <h1>indexh1>

    <p>name: {{.name}}p>
    <p>age: {{.age}}p>

{.users}}

-->
{{range .users}} <p>{{.}}p> {{end}} <img src="/static/images/bg2.jpg" alt=""> body> html>
3、请求参数
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	engine := gin.Default()
    //使用get的方式:http://127.0.0.1:8080/get?name=zhangsan&age=18
	engine.GET("/get", func(c *gin.Context) {
		fmt.Println(c.Query("name"))
		fmt.Println(c.Query("age"))

		c.String(http.StatusOK, "get")
	})

    //使用post的方式
    //如在postman中选择post方式,选择body、form-data输入信息,然后http://127.0.0.1:8080/post
	engine.POST("/post", func(c *gin.Context) {
		fmt.Println(c.PostForm("name"))
		fmt.Println(c.PostForm("age"))

		c.String(http.StatusOK, "post")
	})

    //如:http://127.0.0.1:8080/get_user/12345
	engine.GET("/get_user/:id", func(c *gin.Context) {
		fmt.Println(c.Param("id"))

		c.String(http.StatusOK, "url param")
	})

	engine.Run()
}
insert into student values
(1,'张三',18),

4、参数绑定
package main
import (
	"github.com/gin-gonic/gin"
	"net/http"
)

type User struct {
	Name string `form:"name" binding:"required,len=6"`
	Age int `form:"age" binding:"numeric,min=18,max=100"`
}

func main() {
	engine := gin.Default()
    //http://127.0.0.1:8080/get?name=xxxxxx&age=100
	engine.GET("/get", func(c *gin.Context) {
		var user User
		err := c.ShouldBind(&user)
		if err!=nil {
			c.String(http.StatusOK, err.Error())
		}else{
			c.String(http.StatusOK,"name-> %s age=%d", user.Name,user.Age)
		}
	})
	engine.Run()
}

5、上传文件

(1)上传单个文件


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Titletitle>
head>
<body>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file">
        <input type="submit">
    form>
body>
html>
package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)

func main() {
	engine := gin.Default()
	engine.LoadHTMLGlob("templates/*")

	engine.GET("/upload", func(c *gin.Context) {
		c.HTML(http.StatusOK, "upload.html",nil)
	})

    //http://127.0.0.1:8080/upload
	engine.POST("/upload", func(c *gin.Context) {
		f, err := c.FormFile("file")
		if err!=nil {
			log.Println(err)
		}
        //上传到文件夹uploads下
		err = c.SaveUploadedFile(f, fmt.Sprintf("uploads/%s", f.Filename))
		if err!=nil {
			c.String(http.StatusOK, "上传文件失败->%v",err.Error())
		}else {
			c.String(http.StatusOK, "上传成功")
		}
	})
	engine.Run()
}

(2)上传多个文件

修改form

<input type="file" name="file" multiple="multiple">

修改main.go

engine.POST("/upload", func(c *gin.Context) {
		form, _ := c.MultipartForm()

		files := form.File["file"]
		for _, file := range files{
			c.SaveUploadedFile(file, fmt.Sprintf("uploads/%s", file.Filename))
		}

		c.String(http.StatusOK, "OK")
	})
6、路由组
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	engine := gin.Default()

	api := engine.Group("/api")
	{
        //http://127.0.0.1:8080/api/get_user
		api.GET("/get_user", func(c *gin.Context) {
			c.String(http.StatusOK, "api get_user")
		})

        //http://127.0.0.1:8080/api/get_info
		api.GET("/get_info", func(c *gin.Context) {
			c.String(http.StatusOK, "api get_info")
		})
	}

	admin := engine.Group("/admin")
	{
        //http://127.0.0.1:8080/admin/login
		admin.GET("/login", func(c *gin.Context) {
			c.String(http.StatusOK, "admin login")
		})

        //http://127.0.0.1:8080/admin/logout
		admin.GET("/logout", func(c *gin.Context) {
			c.String(http.StatusOK, "admin logout")
		})
	}

	engine.Run()
}

7、中间件
/**
 * @Author: hq
 * @Date: 2021/3/10 14:07
 * @Description:
 */

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
)

//自定义中间件
func Test() gin.HandlerFunc{
	return func(c *gin.Context) {
		fmt.Println("当前的url是:", c.Request.URL)
	}
}

func main() {
	engine := gin.New()
	//在此使用表示所有请求都经过中间件
	//engine.Use(Test())
	//engine.Use(Test(), gin.Logger())  //同时打印日志

	engine.GET("/get", Test(), func(c *gin.Context) {  //只对某一个请求使用中间件
	//engine.GET("/get", func(c *gin.Context) {
		c.String(http.StatusOK, "get")
	})

	engine.GET("/hello", func(c *gin.Context) {
		c.String(http.StatusOK, "hello")
	})

	//对整个路由组使用中间件
	api := engine.Group("/api", Test())
	{
		api.GET("/get_info", func(c *gin.Context) {
			c.String(http.StatusOK, "api get_info")
		})
		api.GET("/get_user", func(c *gin.Context) {
			c.String(http.StatusOK, "api get_user")
		})
	}
	engine.Run()
}
二、gin框架详解

使用传统go处理请求

/**
 * @Author: hq
 * @Date: 2021/3/15 14:34
 * @Description: web处理请求本质
	使用go处理请求
*/

package main

import (
	"fmt"
	"net/http"
)

func sayHello(w http.ResponseWriter,r *http.Request){
	//使用绝对路径
	//b, _ := ioutil.ReadFile("E:\Go\workspace\study\go-study\static\txt\hello.txt")
	//获取当前文件路径
    cur, _ := os.Getwd()
	filename := filepath.Join(cur, "static/txt/hello.txt")
	b, _ := ioutil.ReadFile(filename)
	_, _ = fmt.Fprintln(w, string(b))

	//_, _ = fmt.Fprintln(w, "hello Golang")
}

func main() {
	http.HandleFunc("/hello",sayHello)
	err := http.ListenAndServe(":9090", nil)
	if err!=nil {
		fmt.Println("http serve failed, err:%v\n", err)
		return
	}
}
Gin框架简单使用

下载并安装

go get -u github.com/gin-gonic/gin

第一个gin示例

package main

import (
	"github.com/gin-gonic/gin"
)

func test(c *gin.Context)  {
	c.JSON(200, gin.H{
		"message": "Hello golang",
	})
}

func main() {
	engine := gin.Default()    //返回默认的路由引擎

	//指定用户使用GET请求访问/hello时,执行test这个函数
	engine.GET("/hello",test)

	//启动服务
	//engine.Run()   //默认8080端口
	engine.Run(":9090")  //指定访问端口
}

api程序的REST风格

Get用来获取资源(查询)POST用来新建资源(创建)PUT用来更新资源(更新)DELETE用来删除资源(删除) 模板与渲染

Go语言内置了文本模板引擎text/template和用于HTML文档的html/template。模板文件中使用{{和}}包裹和标识需要传入的数据,传给模板的数据可以通过**{.}h或者{.FieldName}**来访问它的字段

解析模板文件

package main

import (
	"fmt"
	"html/template"
	"net/http"
	"os"
	"path/filepath"
)

type User struct {
	Name string
	Gender string   //首字母小写访问不到
	Age int
}

func teplatetest(w http.ResponseWriter, r *http.Request){
	//========解析模板========

	//使用绝对路径
	//template, err := template.ParseFiles("E:\Go\workspace\study\go-study\templates\hello.tmpl")
	//获取当前文件路径
	cur, _ := os.Getwd()
	filename := filepath.Join(cur, "templates/hello.tmpl")
	template, err := template.ParseFiles(filename)

	if err != nil{
		fmt.Println("Parse template failed,err:",err)
		return
	}

	//===========渲染模板========
	name := "静静"
	//结构体
	user1 := User{
		Name: "张三",
		Gender: "男",
		Age: 18,
	}
	//map
	map1 := map[string]interface{}{
		"name":   "张三",
		"school": "西华",
	}
	//切片
	hobbies := []string{
		"唱",
		"跳",
		"rap",
	}

	//通过渲染传单个值
	//err = template.Execute(w, name)
	//通过渲染传多个值
	err = template.Execute(w, map[string]interface{}{
		"name" : name,
		"user1" : user1,
		"map1" : map1,
		"hobby" : hobbies,
	})

	if err != nil{
		fmt.Println("render template failed,err:",err)
		return
	}
}

func main() {
	http.HandleFunc("/", teplatetest)
	err := http.ListenAndServe(":9090", nil)
	if err != nil{
		fmt.Println("HTTP serve start failed,err:",err)
		return
	}
}

定义模板文件.tmpl(如templates文件夹下的hello.html)


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Selecttitle>
    <link rel="stylesheet" href="/static/css/index.css">
head>
<body>
<h1>这是查询页面h1>
{{/*获取后端传过来的值*/}}
    <p>Hello: {{.name}}p>
    <p>user1: {{.user1.Name}},{{.user1.Gender}},{{.user1.Age}}p>
    <p>map1: {{.map1.name}},{{.map1.school}}p>
<hr>

{{/*定义变量*/}}
{{ $v1 := 100 }}
{{ $name := .map1.name }}

{{/*if-else*/}}
<p>
    {{ if $v1 }} {{$v1}}
    {{else}}
        v1不存在
    {{end}}
p>
<p>
    {{if $name}} {{$name}}
    {{else}}  age不存在
    {{end}}
p>
<hr>

{{/*函数的使用,如比较大小*/}}
{{if lt .user1.Age 22}} 好好上学
{{else}} 好好工作
{{end}}
<hr>

{{/*range遍历切片*/}}
{{range $idx, $hobby :=.hobby}}
    <p>{{$idx}} - {{$hobby}}p>
{{else}}  没有爱好
{{end}}
<hr>

{{/*with的使用*/}}
{{with .user1}}
<p>{{.Name}},{{.Age}},{{.Gender}}p>
{{end}}
<hr>

{{/*index索引*/}}
{{/*取hobby中索引为2的值*/}}
{{index .hobby 2}}
body>
html>

输出结果如下所示:

这是查询页面

Hello: 静静

user1: 张三,男,18

map1: 张三,西华


100

张三


好好上学


0 - 唱

1 - 跳

2 - rap


张三,18,男


rap

Gin框架模板渲染

首先定义一个存放模板的templates文件夹,然后在其内部按照业务分别定义一个posts文件夹和一个users文件。里面存放index.tmpl


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>posts/indextitle>
head>
<body>
<h1>{{.title}}h1>
body>
html>

模板解析与渲染(包括加载静态文件与给模板添加自定义函数)

package main

import (
	"github.com/gin-gonic/gin"
	"html/template"
	"net/http"
)

//静态文件:HTML页面上用到的样式文件:css,js,图片

func main() {
	engine := gin.Default()

	//加载静态文件
	engine.Static("/xxx","./statics")

	//Gin框架中给模板添加自定义函数
	engine.SetFuncMap(template.FuncMap{
		"safe" : func(str string) template.HTML{
			return template.HTML((str))
		},
	})

	//模板解析
	//engine.LoadHTMLFiles("templates/index.tmpl","templates/users/index.html")
	//加载文件夹下的所有文件(**表目录,*表文件)
	engine.LoadHTMLGlob("templates/**/*")

	engine.GET("/posts/index", func(c *gin.Context) {
		//HTTP请求
		c.HTML(http.StatusOK, "index.tmpl", gin.H{  //模板渲染
			"title" : "posts/index.html",
		})

	})

	engine.GET("/users/index", func(c *gin.Context) {
		//HTTP请求
		c.HTML(http.StatusOK, "index.tmpl", gin.H{  //模板渲染
			"title" : "百度一下",
		})

	})

	//启动serve
	engine.Run(":9090")
}

user/index.html 引用模板函数


<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">

{{/*    引用静态文件,xxx与后台加载时的xxx相对应*/}}
    <link  rel="stylesheet" href="/xxx/test.css">

    <title>posts/indextitle>
head>
<body>

{{/*使用自定义模板函数*/}}
<h1>{{.title | safe}}h1>
body>
html>

Gin框架路由组
package main

import (
	"github.com/gin-gonic/gin"
	"net/http"
)

func main() {
	engine := gin.Default()

	//路由
	//逐个设置请求的方式
	engine.GET("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method" : "GET",
		})

	})
	engine.POST("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK, gin.H{
			"method" : "POST",
		})

	})


	//用Any方法可以匹配所有请求方式
	engine.Any("/test", func(c *gin.Context) {
		switch c.Request.Method {
		case "GET":
			c.JSON(http.StatusOK, gin.H{"method" : "GET"})
		case http.MethodPost:
			c.JSON(http.StatusOK, gin.H{"method" : "POST"})
		//...
		}
	})

	//设置没有路由时访问的页面(以免出现404)
	engine.NoRoute(func(c *gin.Context) {
		c.JSON(http.StatusNotFound, gin.H{"msg" : "该页面不存在,请重新输入!"})
	})

	//路由组
	shopGroup := engine.Group("/shop")
	{
		shopGroup.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg" : "/shop/index"})
		})
		shopGroup.GET("/hello", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg" : "/shop/hello"})
		})
	}
	engine.Run(":9090")
}
Gin框架中间件

Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数,这个钩子就叫做中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等

/**
 * @Author: hq
 * @Date: 2021/3/17 10:53
 * @Description:Gin框架中间件的使用
	注:Gin的中间件必须是一个gin.HandleFunc类型
 */

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"net/http"
	"time"
)

//HandlerFunc
func indexHandler(c *gin.Context)  {
	fmt.Println("这是indexHandler")

	//在上下文中获取值
	name, ok := c.Get("name")
	if !ok{
		name = "匿名用户"
	}

	c.JSON(http.StatusOK, gin.H{
		"msg1" : "index",
		"name" : name,
	})
}

//定义一个中间件
func m1(c *gin.Context)  {
	fmt.Println("这是中间件m1")
}

func m2(c *gin.Context)  {
	fmt.Println("m2 in ...")
	//计时
	start := time.Now()

	//先调用后面需要处理的函数,再执行Next后面的代码
	c.Next()      //调用后续的处理函数

	cost := time.Since(start)
	fmt.Printf("调用耗费时间: %v\n", cost)

	fmt.Println("m2 out ...")
}

func m3(c *gin.Context)  {
	fmt.Println("m3 in..")
	c.Next()

	//c.Abort()     //阻止调用后续处理函数

	//在上下文中设置值
	c.Set("name", "静静")


	//如果想后续代码不执行,可以使用return返回
	//return
	fmt.Println("m3 out..")
}

/*  中间件使用
func authMiddleware(c *gin.Context)  {
	//是否登录的判断
	//if 是登录用户
	//c.Next()
	//else
	//c.Abort()
}
*/

func authMiddleware(doCheck bool)gin.HandlerFunc {
	//可以做一些查询数据库、或者一些其他准备工作
	return func(c *gin.Context) {
		if doCheck{
			//存放具体逻辑
		}else{
			c.Next()  //放行
		}

	}
}

func main() {
	/**gin.Default默认使用了Logger和Recovery中间件
	Logger:将日志写入jinDefaultWriter
	Recovery:中间件会recover任何panic,如果有panic,会写入500响应码
	 */

	engine := gin.Default()

	//如果不想使用默认的中间件
	//engine := gin.New()

	//全局注册中间件
	//engine.Use(m1, authMiddleware)

	//engine.GET("/index", func(c *gin.Context) {
	//	c.JSON(http.StatusOK,  gin.H{
	//		"msg" : "index",
	//	})
	//})

	engine.GET("/index", indexHandler)  //与上面代码作用一致

	//为某个路由单独注册中间件
	engine.GET("/index_m1",m1, indexHandler)  //调用中间件
	engine.GET("/index_m2",m2, indexHandler)
	engine.GET("/index_m3",m2,m3, indexHandler)

	//为路由组注册中间件
	//shopGroup := engine.Group("/shop", authMiddleware(true))
	shopGroup := engine.Group("/shop")
	engine.Use(authMiddleware(true))

	{
		shopGroup.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg" : "shop/index..."})
		})
		shopGroup.GET("/hello", func(c *gin.Context) {
			c.JSON(http.StatusOK, gin.H{"msg" : "shop/hello..."})
		})
	}

	engine.Run(":9090")

}

调用Abort后会阻止调用后面的中间件

调用输出结果:http://127.0.0.1:9090/index_m3

m2 in …
m3 in…
这是indexHandler
m3 out…
调用耗费时间: 0s
m2 out …

注意:当在Gin中间件或Handler中启动新的goroutine中,不能使用原始的上下文(c *gin.Context),必须使用其只读副本(c.Copy())

三、URL处理之URL匹配规则

?匹配一个字符

* 匹配0个或多个字符

* *匹配0个或多个目录

2、例子

/trip/api/*x 匹配 /trip/api/x,/trip/api/ax,/trip/api/abx ;但不匹配 /trip/abc/x;/trip/a/a?x 匹配 /trip/a/abx;但不匹配 /trip/a/ax,/trip/a/abcx/**/api/alie 匹配 /trip/api/alie,/trip/dax/api/alie;但不匹配 /trip/a/api/**/*.htmlm 匹配所有以.htmlm结尾的路径 :xxx模式

使用这种模式,创建的URL必须全匹配xxx。例如

package main
 
import (
	"github.com/gin-gonic/gin"
	"net/http"
)
 
func main() {
	r := gin.Default()
 
	// 可以匹配/user/dongshao这种模式, 但是不能匹配/user/或者/user
	r.GET("/user/:name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "name is %s", name)
	})
 
	r.Run(":8000")
}

*xxx模式

使用这种模式时,创建的URL即可以匹配xxx,也可以不匹配xxx。例如

package main
 
import (
	"github.com/gin-gonic/gin"
	"net/http"
)
 
func main() {
	r := gin.Default()
 
	// 可以匹配/user, /user/, /user/dongshao, /user/dongshao/
	r.GET("/user/*name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "name is %s", name)
	})
 
	r.Run(":8000")
}
:xxx模式和*xxx模式混合在同一个URL
package main
 
import (
	"github.com/gin-gonic/gin"
	"net/http"
)
 
func main() {
	r := gin.Default()
 
	// 只能匹配/user/dongshao/, /user/dongshao/20, /user/dongshao/20/
	// 不能匹配/user/dongshao
	r.GET("/user/:name/*age", func(c *gin.Context) {
		name := c.Param("name")
		age := c.Param("age")
		message := name + " is " + age
		c.String(http.StatusOK, message)
	})
 
	r.Run(":8000")
}

:xxx模式和*xxx模式混合分开使用
package main
 
import (
	"github.com/gin-gonic/gin"
	"net/http"
)
 
func main() {
	r := gin.Default()
 
 
    // /user/dongshao的URL都访问这个
	r.GET("/user/:name", func(c *gin.Context) {
		name := c.Param("name")
		c.String(http.StatusOK, "name is %s", name)
	})
 
    // /user/dongshao/, /user/dongshao/18, /user/dongshao/18/ 的代码都访问这个
	r.GET("/user/:name/*age", func(c *gin.Context) {
		name := c.Param("name")
		age := c.Param("age")
		message := name + " is " + age
		c.String(http.StatusOK, message)
	})
 
	r.Run(":8000")
}

四、路由冲突
panic: wildcard segment ':id' conflicts with existing children in path '/api/v1/devices/:id/sub-devices'

之前运行时出现conflict的错误,这种是因为路径和通配符冲突了

如:

GET /user/info/:name
GET /user/:id
//这种就会产生冲突,例如输出的路由字符串为/user/info,在/user/:id规则中,info会被解释成:id的值

GET /user/info/:name
POST /user/:id
//这种就不会产生冲突,两个热路由的HTTP Method不同

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/996278.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存