栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Gin

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力
Gin_web
  • Gin框架基本使用
  • GORM基本使用
  • WEB开发项目实战
一、关于web
  • Web是使用http协议交互的应用网络
  • Web是通过浏览器/app访问各种资源

一个请求对应一个响应:输入一个url,返回一个页面。

1、使用net/http包:
package main

import (
	"fmt"
	"log"
	"net/http"
)
func sayhello(w http.ResponseWriter,r *http.Request){
	_,err:=fmt.Fprintln(w,"dudu")
	if err != nil {
		log.Println(err)
		return
	}
}
func main(){
	http.HandleFunc("/hello",sayhello)
	err:=http.ListenAndServe(":8080",nil)
	if err != nil {
		log.Println(err)
		return
	}
}

在浏览器上输入:127.0.0.1:8080/hello会显示:dudu

2、创建html文件

dudu

hello Golang!

hello gin!


Gin
package main

import (
	"io/ioutil"
	"log"
	"net/http"
)
func sayhello(w http.ResponseWriter,r *http.Request){
	html,err:=ioutil.ReadFile("./hello.html")
	if err != nil {
		log.Println(err)
		return
	}
	_,_=w.Write(html)
}
func main(){
	http.HandleFunc("/hello",sayhello)
	err:=http.ListenAndServe(":8080",nil)
	if err != nil {
		log.Println(err)
		return
	}
}

在浏览器上输入:127.0.0.1:8080/hello会显示:

图片挂了,更换对应的图片地址即可。

二、Gin框架介绍

框架是为了让我们更高效的开发,专注于开发设计。

Gin是使用Go语言编写的web框架。Go世界里最流行的Web框架,Github上有32K+star。 基于httprouter开发的Web框架。 中文文档齐全,简单易用的轻量级框架。

1、安装
go get -u github.com/gin-gonic/gin
2、基本示例
package main

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

func main() {
	//返回默认的路由引擎
	r :=gin.Default()
	//GET:请求方式;/hello:请求的路径
	//客户端浏览器等访问/hello路径时,会执行后面的匿名函数
	r.GET("/book", func(c *gin.Context) {
		c.JSON(200,gin.H{
			"msg":"GET",
		})
	})
	
	r.POST("/book", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"POST",
		})
	})
	
	r.PUT("/book", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"PUT",
		})
	})

	r.DELETE("/book", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"DELETE",
		})
	})
	//启动http服务,默认端口8080
	err:=r.Run()
	if err != nil {
		log.Println(err)
		return
	}
}

从上面的示例可知,gin框架支持RESTful API,简单来说就是:

客户端和web服务器交互的时候,使用HTTP协议中的四个请求方法代表不同的动作。

  • GET用来获取资源

  • POST用来新建资源

  • PUT用来更新资源

  • DELETE用来删除资源

    只要API程序遵循了REST风格,就称其为RESTful API。

    举例:现在编写一个图书管理系统,需要对书籍进行增删改查,编写程序时设计客户端和web服务器交互的方式,通常会这么设计:

    请求方法URL含义
    GET/book查询书籍信息
    POST/create_book创建书籍记录
    POST/updata_book更新书籍信息
    POST/delete_book删除书籍信息

    现在使用RESTful 风格:

    请求方法URL含义
    GET/book查询书籍信息
    POST/book创建书籍信息
    PUT/book更新书籍信息
    DELETE/book删除书籍信息
三、模板渲染

html/template包实现了数据驱动的模板,用于生成可防止代码注入的安全的HTML内容。它提供了和text/template包相同的接口,Go语言中输出HTML的场景都应使用html/template这个包。

1、go语言模版引擎

Go语言内置了文本模板引擎text/template和用于HTML文档的html/template。它们的作用机制可以简单归纳如下:

  1. 模板文件通常定义为.tmpl和.tpl为后缀(也可以使用其他的后缀),必须使用UTF8编码。
  2. 模板文件中使用{{和}}包裹和标识需要传入的数据。
  3. 传给模板这样的数据就可以通过点号(.)来访问,如果数据是复杂类型的数据,可以通过{ { .FieldName }}来访问它的字段。
  4. 除{{和}}包裹的内容外,其他内容均不做修改原样输出。
2、模版引擎的使用
  • 定义模板文件

    根据相关衣服啊规则去编写。

  • 解析模板文件

  func (t *Template) Parse(src string) (*Template, error)
func ParseFiles(filenames ...string) (*Template, error)
func ParseGlob(pattern string) (*Template, error)
  • 模板渲染

​ 简单来说就是使用数据去填充模板

func (t *Template) Execute(wr io.Writer, data interface{}) error
func (t *Template) ExecuteTemplate(wr io.Writer, name string, data interface{}) error
3、基本示例

模板语法都包含在{{和}}中间,其中{{.}}中的点表示当前对象。

当我们传入一个结构体对象时,我们可以根据.来访问结构体的对应字段。例如:




    
    
    
    Document


Hello {{ . }}

代码示例:

package main

import (
	"html/template"
	"log"
	"net/http"
)

func sayhello(w http.ResponseWriter,r *http.Request){
	//1.定义模板
	//2.解析模板
	t,err:=template.ParseFiles("./hello.tmpl")
	if err != nil {
		log.Println("Parse template failed,err:",err)
		return
	}
	//3.渲染模板
	name:="dudu"
	err=t.Execute(w,name)
	if err != nil {
		log.Println("exec failed,err:",err)
		return
	}
}

func main() {
	http.HandleFunc("/hello",sayhello)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4、Template模板



    
    
    
    Document


u1

Hello {{- .u1.Name -}}

性别: {{- .u1.Gender -}}

年龄 :{{- .u1.Age -}}

{{}}

m1

hello {{ .m1.name }}

性别:{{ .m1.Gender }}

年龄:{{ .m1.Age }}


{{}} {{ $v1:=100 }} {{ $age:=.m1.Age }}

{{ $age }}


{{}} {{ if $v1 }} {{ $v1 }} {{else}} 无 {{end}}
{{}} {{ if lt .m1.Age 22 }} 好好上学 {{else}} 好好工作 {{end}}
{{}} {{ range $index,$value := .hobby }}

{{$index}} - {{$value}}

{{else}} 无爱好 {{end}}
{{}}

m1

{{ with .m1}}

hello {{ .name }}

性别:{{ .Gender }}

年龄:{{ .Age }}

{{end}}
{{}} {{index .hobby 2}}

代码:

package main

import (
	"html/template"
	"log"
	"net/http"
)
type User struct {
	Name string
	Gender string
	Age int
}
func sayhello(w http.ResponseWriter,r *http.Request){
	//1.定义模板
	//2.解析模板
	t,err:=template.ParseFiles("./hello.tmpl")
	if err != nil {
		log.Println("Parse failed,err:",err)
		return
	}
	//3.渲染模板
	u1:=User{
		Name: "嘟嘟",
		Gender: "女",
		Age: 3,
	}
	m1:=map[string]interface{}{
		"name":"嘟嘟",
		"Gender":"女",
		"Age":18,
	}
	hobbyList:=[]string{
		"篮球",
		"足球",
		"双色球",
	}
	err=t.Execute(w,map[string]interface{}{
		"u1":u1,
		"m1":m1,
		"hobby":hobbyList,
	})
	if err != nil {
		log.Println("exec failed,err:",err)
		return
	}
}

func main() {
	http.HandleFunc("/hello",sayhello)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4.1自定义模板

Html:




    
    
    
    自定义模板函数


    {{ kua . }}


代码

package main

import (
	"html/template"
	"log"
	"net/http"
)

func f1(w http.ResponseWriter,r *http.Request){
	//定义一个函数kua
	kua:= func(name string)(string,error){
		//要么只有一个返回值;有两个返回值第二个必须是error类型
		return name+"真可爱",nil
	}
	//1.定义模板

	t:=template.New("f.tmpl") //创建一个名字是f.tmpl的模板对象
	//告诉模板引擎,现在多了一个自定义函数kua
	t.Funcs(template.FuncMap{
		"kua":kua,
	})

	//2.解析模板
	_,err:=t.ParseFiles("./f.tmpl")  //要与上面的f.tmpl一致
	if err != nil {
		log.Println("parse failed,err:",err)
		return
	}
	//3.渲染模板
	name:="嘟嘟"
	err=t.Execute(w,name)
	if err != nil {
		log.Println(err)
		return
	}
}

func main() {
	http.HandleFunc("/hello",f1)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4.2模板嵌套

先定义两个tmpl文件

t .tmpl




    
    
    
    tmpl test



测试嵌套template语法


{{}} {{template "ul.tmpl"}}
{{}} {{template "ol.tmpl"}}
你好,{{ . }}
{{}} {{ define "ol.tmpl"}}
  1. 吃饭
  2. 睡觉
  3. 打豆豆
{{end}}

ul.tmpl

  • 注释
  • 日志
  • 测试

代码:

package main

import (
	"html/template"
	"log"
	"net/http"
)


func demo1(w http.ResponseWriter,r *http.Request){
	t,err:=template.ParseFiles("./t.tmpl","./ul.tmpl")
	if err != nil {
		log.Println("parse2 failed,err:",err)
		return
	}
	name:="dudu"
	err=t.Execute(w,name)
	if err != nil {
		log.Println(err)
		return
	}
}

func main() {
	http.HandleFunc("/tmplDemo",demo1)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4.3模板继承block

引入:

先定义两个模板:

index.tmpl




    
    模板继承
    



这是index页面

Hello {{.}}

home.index:




    
    模板继承
    



这是home页面

Hello {{.}}

代码:

package main

import (
	"html/template"
	"log"
	"net/http"
)

func index(w http.ResponseWriter,r*http.Request){
	//定义模板
	//解析模板
	t,err:=template.ParseFiles("./index.tmpl")
	if err != nil {
		log.Println("parse tmpl1 failed,err:",err)
		return
	}
	msg:="dudu"
	//渲染模板
	err=t.Execute(w,msg)
	if err != nil {
		log.Println("exec index failed,err:",err)
		return
	}
}

func home(w http.ResponseWriter,r*http.Request){
	//定义模板
	//解析模板
	t,err:=template.ParseFiles("./home.tmpl")
	if err != nil {
		log.Println("parse tmpl2 failed,err:",err)
		return
	}
	msg:="dudu"
	//渲染模板
	err=t.Execute(w,msg)
	if err != nil {
		log.Println("exec home failed,err:",err)
		return
	}
}

func main() {
	http.HandleFunc("/index",index)
	http.HandleFunc("/home",home)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

可以看到两个模板的内容基本上是一模一样,这时候就可以使用block:典型的用法是定义一组根模板,然后通过在其中重新定义块模板进行自定义。

定义三个tmpl文件

base.tmpl




    
    模板继承
    



{{block "content" .}}{{end}}

index.tmpl

{{}}
{{template "base.tmpl" .}}
{{}}
{{define "content"}}
    

index2页面

Hello {{ . }}

{{end}}

home.tmpl

{{}}
{{template "base.tmpl" .}}
{{}}
{{define "content"}}
    

home2页面

Hello {{.}}

{{end}}

代码:

package main

import (
	"html/template"
	"log"
	"net/http"
)

func index(w http.ResponseWriter,r*http.Request){
	//定义模板(模板继承的方式)
	//解析模板
	t,err:=template.ParseFiles("./template/base.tmpl","./template/index.tmpl")
	if err != nil {
		log.Println("parse failed,err:",err)
		return
	}
	//渲染模板
	name:="嘟嘟"
	err=t.ExecuteTemplate(w,"index.tmpl",name)
	if err != nil {
		log.Println("exec failed,err:",err)
		return
	}
}

func home(w http.ResponseWriter,r*http.Request){
	//定义模板(模板继承的方式)
	//解析模板
	t,err:=template.ParseFiles("./template/base.tmpl","./template/home.tmpl")
	if err != nil {
		log.Println("parse failed,err:",err)
		return
	}
	//渲染模板
	name:="一一"
	err=t.ExecuteTemplate(w,"home.tmpl",name)
	if err != nil {
		log.Println("exec failed,err:",err)
		return
	}
}

func main() {
	http.HandleFunc("/index",index)
	http.HandleFunc("/home",home)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4.4模板补充

创建一个xss.tmpl:




    
    
    
    修改模板引擎标识符号


Hello {{ .str1 }}
Hello {{ .str2 |safe}}


主函数:

package main

import (
	"html/template"
	"log"
	"net/http"
)

func xss(w http.ResponseWriter,r *http.Request){
	//定义模板
	//解析模板
	t,err:=template.New("xss.tmpl").Funcs(template.FuncMap{
		"safe": func(s string) template.HTML{
			return template.HTML(s)
		},
	}).ParseFiles("./xss.tmpl")
	if err != nil {
		log.Println("parse xss failed,err:",err)
		return
	}
	//渲染模板
	str1:=""
	str2 := "liwenzhou的博客"
	err=t.Execute(w,map[string]interface{}{
		"str1":str1,
		"str2":str2,

	})
	if err != nil {
		log.Println("exec xss failed,err:",err)
		return
	}

}

func main() {
	http.HandleFunc("/xss",xss)
	err:=http.ListenAndServe(":9000",nil)
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

执行后发现liwenzhou的博客是可以点击链接的,是因为我们在Hello {{ .str2 |safe}}加了safe,需要提前在主函数中定义,且必须在解析函数前定义:

t,err:=template.New("xss.tmpl").Funcs(template.FuncMap{
		"safe": func(s string) template.HTML{
			return template.HTML(s)
		},
	}).ParseFiles("./xss.tmpl")
四、Gin渲染 1、HTML渲染

在templates下定义两个目录posts/index.tmpl和users/index.tmpl内容如下:

posts/index.tmpl:

{{define "posts/index.tmpl"}}
    
    

    
        
        
        
        posts/index
    
    
    {{.title}}
    
    
{{end}}

users/index.tmpl:

{{define "users/index.tmpl"}}
    
    
    
        
        
        
        users/index
    
    
    {{.title}}
    
    
{{end}}

代码:

package main

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

func main() {
	r:=gin.Default()
	//定义模板
	//解析模板
	//r.LoadHTMLFiles("templates/posts/index.tmpl","templates/users/index.tmpl")
	r.LoadHTMLGlob("templates*") /*")     /*") //**表示目录;*表示文件

	//渲染模板

	r.GET("/users/index", func(c *gin.Context) {
		c.HTML(http.StatusOK,"users/index.tmpl",gin.H{
			"title":"李文周的博客",
		})
	})
	
	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4、JSON渲染

代码:

package main

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


func main() {
	r:=gin.Default()
	r.GET("/json", func(c *gin.Context) {
		//1.使用map
		data1 := map[string]interface{}{
			"name": "dudu",
			"msg":  "hello",
			"age":  3,
		}
		c.JSON(http.StatusOK,data1)
	})

	r.GET("/json1", func(c *gin.Context) {
		//等同于map
		data2:=gin.H{
			"name":"yiyi",
			"msg":"hello",
			"age":2,
		}
		c.JSON(http.StatusOK,data2)
	})

	r.GET("/json2", func(c *gin.Context) {
		//2.使用结构体
		type data struct{
			Name string   `json:"name"`     //灵活使用tag来对结构字字段做定制化操作
			Age int        `json:"xxx"`
			Msg string
		}

		d:=data{
			Name:"8hao",
			Age:1,
			Msg:"hello",
		}
		c.JSON(http.StatusOK,d)      //json的序列化,字段名必须大写才能导出。
	})
	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

总结:一般有两种方法一种是map,一种是结构体;

gin内置了一种gin.H等同于map。

5、XML渲染
package main

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


func main() {
	r := gin.Default()
	// gin.H 是map[string]interface{}的缩写
	r.GET("/someXML", func(c *gin.Context) {
		// 方式一:自己拼接JSON
		c.XML(http.StatusOK, gin.H{"message": "Hello world!"})
	})
	r.GET("/moreXML", func(c *gin.Context) {
		// 方法二:使用结构体
		type MessageRecord struct {
			Name    string
			Message string
			Age     int
		}
		var msg MessageRecord
		msg.Name = "小王子"
		msg.Message = "Hello world!"
		msg.Age = 18
		c.XML(http.StatusOK, msg)
	})
	r.Run(":8080")
}
6、YAML渲染
package main

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


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

	r.GET("/someYAML", func(c *gin.Context) {
		c.YAML(http.StatusOK, gin.H{"message": "ok", "status": http.StatusOK})
	})
	r.Run(":8080")
}
7、protobuf渲染
package main

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


func main() {
	r := gin.Default()
	r.GET("/someProtoBuf", func(c *gin.Context) {
		reps := []int64{int64(1), int64(2)}
		label := "test"
		// protobuf 的具体定义写在 testdata/protoexample 文件中。
		data := &protoexample.Test{
			Label: &label,
			Reps:  reps,
		}
		// 请注意,数据在响应中变为二进制数据
		// 将输出被 protoexample.Test protobuf 序列化了的数据
		c.ProtoBuf(http.StatusOK, data)
	})
	r.Run(":8080")
}
五、获取参数 1、获取querystring参数

代码:

package main

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

func main() {
	r:=gin.Default()
	//querystring

	//GET请求URL ?后面的是querystring等参数
	//key=value,多个key-value用 & 符号连接
	//eq: /web?query=yiyi&age=3
	r.GET("/web", func(c *gin.Context) {
		//获取浏览器那边发请求携带的query string 参数
		//name:=c.Query("query")                             //1.通过Query获取请求中携带的querystring参数

		name:=c.DefaultQuery("query","嘟嘟")  //2.取不到就用指定的默认值
		age:=c.DefaultQuery("age","3")

		//name,ok:=c.GetQuery("query")                       //3.取到返回(值,true)取不到第二个参数就返回("",false)
		//if !ok{
		//	//取不到
		//	name="嘟嘟"
		//}
		c.JSON(http.StatusOK,gin.H{
			"name":name,
			"age":age,
		})
	})

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
2、获取form参数

新建两个hmtl文件

login.html:




    
    login


index.html:




    
    index


Hello {{ .Name }}!

你的密码是:{{ .Password }}

代码:

package main

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

//获取form表单提交的参数

func main(){
	r:=gin.Default()
	r.LoadHTMLFiles("./login.html","index.html")

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

	//login posts请求
	r.POST("/login", func(c *gin.Context) {
		//获取form表单提交的数据
		//方法1
		//username:=c.PostForm("username")   //取到返回值,取不到返回空字符串
		//password:=c.PostForm("password")

		//方法2
		//username:=c.DefaultPostForm("username","somebody")
		//password:=c.DefaultPostForm("zzz","***")

		//方法3
		username,ok:=c.GetPostForm("username")
		if !ok{
			username="sb"
		}

		password,ok:=c.GetPostForm("password")
		if !ok{
			password="123566"
		}


		c.HTML(http.StatusOK,"index.html",gin.H{
			"Name":username,
			"Password":password,
		})
	})

	
	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

切记:一个请求对应一个响应。

第一次返回get方法;点击登陆后返回post方法。

3、获取path/URL参数

代码:

package main

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

//获取请求的path(URL)参数,返回的都是字符串类型
//注意URL的匹配不要冲突

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

	r.GET("/user/:name/:age/:ds", func(c *gin.Context) {
		//获取参数路径
		name:=c.Param("name")
		age:=c.Param("age")   //string类型
		ds:=c.Param("ds")
		c.JSON(http.StatusOK,gin.H{
			"name":name,
			"age":age,
			"ds":ds,
		})
	})

	r.GET("/blog/:year/:month", func(c *gin.Context) {
		year:=c.Param("year")
		month:=c.Param("month")
		c.JSON(http.StatusOK,gin.H{
			"year":year,
			"month":month,
		})
	})

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
4、参数绑定

之前的方法:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)
type UserInfo struct {
	username string
	password string

}
func main() {
	r:=gin.Default()
	r.GET("/user", func(c *gin.Context) {
		username:=c.Query("username")
		password:=c.Query("password")
		u:=UserInfo{
			username,
			password,
		}
		fmt.Printf("%#vn",u)
		c.JSON(http.StatusOK,gin.H{
			"msg":"ok",
		})
	})

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}

现在用结构体:

package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"log"
	"net/http"
)
type UserInfo struct {
	Username string  `form:"username" json:"user"`
	Password string	 `form:"password" json:"pwd"`

}
func main() {
	r:=gin.Default()

	r.GET("/user", func(c *gin.Context) {

		var u UserInfo  //声明一个UserInfo类型的变量
		err:=c.ShouldBind(&u)    //传指针
		if err != nil {
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
		}else {
			fmt.Printf("%#vn",u)
			c.JSON(http.StatusOK,gin.H{
				"status":"ok",
			})
		}
	})

	r.POST("/form", func(c *gin.Context) {

		var u UserInfo  //声明一个UserInfo类型的变量
		err:=c.ShouldBind(&u)    //传指针
		if err != nil {
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
		}else {
			fmt.Printf("%#vn",u)
			c.JSON(http.StatusOK,gin.H{
				"status":"ok",
			})
		}
	})

	r.POST("/json", func(c *gin.Context) {

		var u UserInfo  //声明一个UserInfo类型的变量
		err:=c.ShouldBind(&u)    //传指针
		if err != nil {
			c.JSON(http.StatusBadRequest,gin.H{
				"error":err.Error(),
			})
		}else {
			fmt.Printf("%#vn",u)
			c.JSON(http.StatusOK,gin.H{
				"status":"ok",
			})
		}
	})

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}

.ShouldBind()函数功能强大,能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。

5、文件上传 5.1单个文件上传

新建一个index.html:




    
    index



代码:

package main

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

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

	r.LoadHTMLGlob("./index.html")

	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{
				"error":err.Error(),
			})
		}else {
			//从请求中读取文件,将读取到的文件保存在本地(服务端本地)
			//filePath:=fmt.Sprintf("./%s",f.Filename)
			filePath:=path.Join("./",f.Filename)
			_=c.SaveUploadedFile(f,filePath)
			c.JSON(http.StatusOK,gin.H{
				"status":"ok",
			})
		}

	})

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

5.2多个文件上传

新建一个index.html:




    
    
    
    Document


上传文件:

代码:

package main

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

// gin的helloWorld

func main() {
	// 1.创建路由
	// 默认使用了2个中间件Logger(), Recovery()
	r := gin.Default()
	// 限制表单上传大小 8MB,默认为32MB
	r.LoadHTMLGlob("./index.html")

	r.GET("/index", func(c *gin.Context) {
		c.HTML(http.StatusOK,"index.html",nil)
	})
  
	r.MaxMultipartMemory = 8 << 20
	r.POST("/upload", func(c *gin.Context) {
		form, err := c.MultipartForm()
		if err != nil {
			c.String(http.StatusBadRequest, fmt.Sprintf("get err %s", err.Error()))
		}
		// 获取所有图片
		files := form.File["files"]
		// 遍历所有图片
		for _, file := range files {
			// 逐个存
			if err := c.SaveUploadedFile(file, file.Filename); err != nil {
				c.String(http.StatusBadRequest, fmt.Sprintf("upload err %s", err.Error()))
				return
			}
		}
		c.String(200, fmt.Sprintf("upload ok %d files", len(files)))
	})
	//默认端口号是8080
	r.Run(":8000")
}
6、重定向 6.1HTTP重定向
package main

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

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

	r.GET("/index", func(c *gin.Context) {
		//c.JSON(http.StatusOK,gin.H{
		//	"status":"ok",
		//})
		//跳转到sogo.com
		c.Redirect(http.StatusMovedPermanently,"http://www.sogo.com")
	})

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}

6.2路由重定向
package main

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

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

	r.GET("/a", func(c *gin.Context) {
		//跳转到/b对应的处理路由函数
		c.Request.URL.Path="/b"    //把请求的URL修改
		r.HandleContext(c)     //继续后续的处理

	})

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

	err:=r.Run(":9090")
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}
六、Gin路由 1、普通路由
package main

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

func main() {
	r:=gin.Default()
	//访问/index的GET请求会走这一条处理逻辑
	//路由
	r.GET("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"method":"GET",
		})
	})
	r.POST("/index", func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"method":"POST",
		})
	})

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

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

	
	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}

这种方法是GET、POST、PUT、DELETE分开写的,比较易理解但是繁琐。

下面使用r.Any方法,可以匹配所有情的方法:

package main

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

func main() {
	r:=gin.Default()
	//访问/index的GET请求会走这一条处理逻辑
	//路由

	r.Any("/user", 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",
			})
		case http.MethodPut:
			c.JSON(http.StatusOK,gin.H{
			"method":"PUT",
			})
		case http.MethodDelete:
			c.JSON(http.StatusOK,gin.H{
			"method":"DELETE",
			})
		default:
			fmt.Println("dudu")

		}

	})
	
	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}
2、路由组
package main

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

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

	//路由组的组
	//把公用的前缀提取出来,创建一个路由组
	
	userGroup:=r.Group("/user")
	{
		userGroup.GET("/index", func(c *gin.Context) {})
		userGroup.GET("/login", func(c *gin.Context) {})
		userGroup.POST("/login", func(c *gin.Context) {})
	}
	
	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) {})
	}
	
	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}

}

路由组:有共同URL前缀的路由划分为一个路由组。用一对{}包裹同组的路由

七、中间件 1、中间件概念

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gNHoTxoB-1658822781066)(/Users/xujiaxin/Desktop/picture/中间件.png)]

中间件适合处理一些公共的业务逻辑

2、中间件一般写法和处理方法

写法一:m1和想要处理函数写在一起

package main

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

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

//定义一个中间件m1:统计处理请求函数的耗时
func m1(c *gin.Context){
	fmt.Println("m1 in ...")
	start:=time.Now()
	fmt.Println("start:",start)
	c.Next()
	cost:=time.Since(start)
	fmt.Printf("cost:%vn",cost)
	fmt.Println("m1 out ...")

}

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

	r.GET("/index", m1,indexHandler)

	r.GET("/shop",m1, func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"shop",
		})
	})
	r.GET("/user",m1, func(c *gin.Context) {
		c.JSON(http.StatusOK,gin.H{
			"msg":"user",
		})
	})


	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

写法二:

package main

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

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

//定义一个中间件m1:统计处理请求函数的耗时
func m1(c *gin.Context){
	fmt.Println("m1 in ...")
	start:=time.Now()
	fmt.Println("start:",start)
	c.Next()
	cost:=time.Since(start)
	fmt.Printf("cost:%vn",cost)
	fmt.Println("m1 out ...")

}

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

	r.Use(m1)

	r.GET("/index", indexHandler)

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

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


	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

如果有两个中间件分别调用了c.Next()执行顺序是怎么样的?

package main

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

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

//定义一个中间件m1:统计处理请求函数的耗时
func m1(c *gin.Context){
	fmt.Println("m1 in ...")
	start:=time.Now()
	c.Next()
	cost:=time.Since(start)
	fmt.Printf("cost:%vn",cost)
	fmt.Println("m1 out ...")

}

func m2(c *gin.Context){
	fmt.Println("m2 in ...")
	c.Next()
	fmt.Println("m2 out ...")
}

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

	r.Use(m1,m2)

	r.GET("/index", indexHandler)

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

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


	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

以下就是执行顺序,大家自己思考下为什么:

m1 in ...
m2 in ...
index...
m2 out ...
cost:143.208µs
m1 out ...
[GIN] 2022/07/15 - 16:16:15 | 200 |     191.167µs |       127.0.0.1 | GET      "/index"

调用c.Abort:阻止调用后续函数

package main

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

func indexHandler(c *gin.Context){
	fmt.Println("index...")
	c.JSON(http.StatusOK,gin.H{
		"msg":"index",
	})
}

//定义一个中间件m1:统计处理请求函数的耗时
func m1(c *gin.Context){
	fmt.Println("m1 in ...")
	start:=time.Now()
	c.Next()   //调用后续的处理函数
	cost:=time.Since(start)
	fmt.Printf("cost:%vn",cost)
	fmt.Println("m1 out ...")

}

func m2(c *gin.Context){
	fmt.Println("m2 in ...")
	c.Abort()   //阻止调用后续的处理函数
	// return 加上的话,立即结束m2函数
	fmt.Println("m2 out ...")
}

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

	r.Use(m1,m2)

	r.GET("/index", indexHandler)

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

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


	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

执行结果:

m1 in ...
m2 in ...
m2 out ...
cost:3.583µs
m1 out ...

可以看到浏览器也不会返回任何信息,因为indexHandler没有被执行。

3、业务具体写法
package main

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

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

//定义一个中间件m1:统计处理请求函数的耗时
func m1(c *gin.Context){
	fmt.Println("m1 in ...")
	start:=time.Now()
	c.Next()   //调用后续的处理函数
	cost:=time.Since(start)
	fmt.Printf("cost:%vn",cost)
	fmt.Println("m1 out ...")

}

func  authMiddleWare(doCheck bool)gin.HandlerFunc {
	//查询/连接数据库
	//其他的一些准备参数
	return func(c *gin.Context) {
		if doCheck{
			//存放具体的逻辑
			//是否登陆的判断
			//if 是登陆用户
			//c.Next()
			//else
			//c.Abort()
		}else{
			c.Next()
		}

	}
}
func main() {
	r:=gin.Default()

	r.Use(m1,authMiddleWare(true))

	r.GET("/index", indexHandler)

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

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


	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

一般写成闭包的形式

4、路由组注册中间件
package main

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

func  authMiddleWare(doCheck bool)gin.HandlerFunc {
	//查询/连接数据库
	//其他的一些准备参数
	return func(c *gin.Context) {
		if doCheck{
			//存放具体的逻辑
			//是否登陆的判断
			//if 是登陆用户
			//c.Next()
			//else
			//c.Abort()
		}else{
			c.Next()
		}

	}
}
func main() {
	r:=gin.Default()

	//路由组注册中间件方法1:
	xxGroup:=r.Group("/xx",authMiddleWare(true))
	{
		xxGroup.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK,gin.H{
				"msg":"xxGroup",
			})
		})
	}

	//路由组注册中间件方法2:
	xx2Group:=r.Group("/xx")
	xx2Group.Use(authMiddleWare(true))
	{
		xx2Group.GET("/index", func(c *gin.Context) {
			c.JSON(http.StatusOK,gin.H{
				"msg":"xx2Group",
			})
		})
	}


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


	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}
注意事项一、
package main

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

func indexHandler(c *gin.Context){
	fmt.Println("index...")
	name,ok:=c.Get("name")
	if !ok {
		name="匿名用户"
	}
	c.JSON(http.StatusOK,gin.H{
		"msg":name,
	})
}

//定义一个中间件m1:统计处理请求函数的耗时
func m1(c *gin.Context){
	fmt.Println("m1 in ...")
	start:=time.Now()
	c.Next()   //调用后续的处理函数
	cost:=time.Since(start)
	fmt.Printf("cost:%vn",cost)
	fmt.Println("m1 out ...")

}

func m2(c *gin.Context){
	fmt.Println("m2 in ...")
	//c.Abort()   //阻止调用后续的处理函数
	c.Set("name","dudu")
	// return 加上的话,立即结束m2函数
	fmt.Println("m2 out ...")
}



func  authMiddleWare(doCheck bool)gin.HandlerFunc {
	//查询/连接数据库
	//其他的一些准备参数
	return func(c *gin.Context) {
		if doCheck{
			//存放具体的逻辑
			//是否登陆的判断
			//if 是登陆用户
			//c.Next()
			c.Next()
			//else
			//c.Abort()
		}else{
			c.Next()
		}

	}
}
func main() {
	r:=gin.Default()

	r.Use(m1,m2,authMiddleWare(true))

	r.GET("/index", indexHandler)

	err:=r.Run()
	if err != nil {
		log.Println("http server start failed,err:",err)
		return
	}
}

m2中的c.Set("name","dudu")在处理函数中调用:

func indexHandler(c *gin.Context){
	fmt.Println("index...")
	name,ok:=c.Get("name")
	if !ok {
		name="匿名用户"
	}
	c.JSON(http.StatusOK,gin.H{
		"msg":name,
	})
}
注意事项二、
package main

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

func main() {
	//返回默认的路由引擎
	//r :=gin.Default()   //默认调用了Logger()和Recovery()
	r:=gin.New()
	//GET:请求方式;/hello:请求的路径
	//客户端浏览器等访问/hello路径时,会执行后面的匿名函数
	r.GET("/book", func(c *gin.Context) {
		c.JSON(200,gin.H{
			"msg":"GET",
		})
	})


	//启动http服务,默认端口8080
	err:=r.Run()
	if err != nil {
		log.Println(err)
		return
	}
} 

gin.Default()默认使用了Logger和Recovery中间件,其中:

  • Logger中间件将日志写入gin.DefaultWriter,即使配置了GIN_MODE=release。
  • Recovery中间件会recover任何panic。如果有panic的话,会写入500响应码。

如果不想使用上面两个默认的中间件,可以使用gin.New()新建一个没有任何默认中间件的路由。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1027825.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号