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") //指定访问端口
}
Get用来获取资源(查询)POST用来新建资源(创建)PUT用来更新资源(更新)DELETE用来删除资源(删除) 模板与渲染api程序的REST风格
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>
输出结果如下所示:
Gin框架模板渲染这是查询页面
Hello: 静静
user1: 张三,男,18
map1: 张三,西华
100
张三
好好上学
0 - 唱
1 - 跳
2 - rap
张三,18,男
rap
首先定义一个存放模板的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不同
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)