[TOC]
## Gin安装
配置代理:[https://goproxy.cn,direct](https://goproxy.cn,direct)
> go get -u github.com/gin-gonic/gin
## 入门案例
~~~
package main
import (
"github.com/gin-gonic/gin"
)
func main() {
// 创建一个默认的路由引擎
r := gin.Default()
// GET:请求方式;/hello:请求的路径
// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数
r.GET("/hello", func(c *gin.Context) {
// c.JSON:返回JSON格式的数据
c.JSON(200, gin.H{
"message": "Hello world!",
})
})
// 默认8080,括号里的参数是端口号
r.Run()
}
~~~
## Gin渲染
### HTML渲染
我们首先定义一个存放模板文件的`templates`文件夹,然后在其内部按照业务分别定义一个`posts`文件夹和一个`users`文件夹。 `posts/index.html`文件的内容如下:
~~~
{{define "posts/index.html"}}
<!DOCTYPE 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">
<title>posts/index</title>
</head>
<body>
{{.title}}
</body>
</html>
{{end}}
~~~
`users/index.html`文件的内容如下:
~~~
{{define "users/index.html"}}
<!DOCTYPE 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">
<title>users/index</title>
</head>
<body>
{{.title}}
</body>
</html>
{{end}}
~~~
Gin框架中使用`LoadHTMLGlob()`或者`LoadHTMLFiles()`方法进行HTML模板渲染。
~~~
func main() {
r := gin.Default()
r.LoadHTMLGlob("templates/**/*")
//r.LoadHTMLFiles("templates/posts/index.html", "templates/users/index.html")
r.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.html", gin.H{
"title": "posts/index",
})
})
r.GET("users/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "users/index.html", gin.H{
"title": "users/index",
})
})
r.Run(":8080")
}
~~~
## 获取参数
### 获取querystring参数
`querystring`指的是URL中`?`后面携带的参数,例如:`/user/search?username=小王子&address=沙河`。 获取请求的querystring参数的方法如下:
~~~
func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search", func(c *gin.Context) {
// 可以添加默认值
username := c.DefaultQuery("username", "小王子")
//username := c.Query("username")
address := c.Query("address")
//输出json结果给调用方
c.JSON(http.StatusOK, gin.H{
"message": "ok",
"username": username,
"address": address,
})
})
r.Run()
}
~~~
~~~
http://localhost:8080/user/search?username=小王子&address=沙河
~~~
### 获取path参数
请求的参数通过URL路径传递,例如:`/user/search/小王子/沙河`。 获取请求URL路径中的参数的方式如下。
~~~
func main() {
//Default返回一个默认的路由引擎
r := gin.Default()
r.GET("/user/search/:username/:address", func(c *gin.Context) {
username := c.Param("username")
address := c.Param("address")
//输出json结果给调用方
c.JSON(http.StatusOK, gin.H{
"message": "ok",
"username": username,
"address": address,
})
})
r.Run(":8080")
}
~~~
## 文件上传
### 单文件上传
写个前端页面,结构
![](https://img.kancloud.cn/9c/f0/9cf09c185aa2ea0d07a753d11945c385_330x304.png)
~~~
{{define "uploads/upload.html"}}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>上传文件示例</title>
</head>
<body>
<form action="/uploads/upload" method="post" enctype="multipart/form-data">
<input type="file" name="f1">
<input type="submit" value="上传">
</form>
</body>
</html>
{{end}}
~~~
后台:
~~~
func main() {
router := gin.Default()
//模板路径
router.LoadHTMLGlob("templates/**/*")
r.GET("/upload/index", func(c *gin.Context) {
//页面路径
c.HTML(200,"uploads/upload.html",nil)
})
// 处理multipart forms提交文件时默认的内存限制是32 MiB
// 可以通过下面的方式修改
// router.MaxMultipartMemory = 8 << 20 // 8 MiB
router.POST("/upload", func(c *gin.Context) {
// 单个文件
file, err := c.FormFile("f1")
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{
"message": err.Error(),
})
return
}
log.Println(file.Filename)
dst := fmt.Sprintf("C:/tmp/%s", file.Filename)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("'%s' uploaded!", file.Filename),
})
})
router.Run()
}
~~~
### 多文件上传
input选择框里面加个`multiple`表示你可以多选
~~~
{{define "uploads/uploads.html"}}
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<title>上传文件示例</title>
</head>
<body>
<form action="/uploads" method="post" enctype="multipart/form-data">
<input type="file" name="files" multiple>
<input type="submit" value="上传">
</form>
</body>
</html>
{{end}}
~~~
后台:
~~~
r.POST("/uploads", func(c *gin.Context) {
// Multipart form
form, _ := c.MultipartForm()
files := form.File["files"]
for _, file := range files {
log.Println(file.Filename)
dst := fmt.Sprintf("D:/%s", file.Filename)
// 上传文件到指定的目录
c.SaveUploadedFile(file, dst)
}
c.JSON(http.StatusOK, gin.H{
"message": fmt.Sprintf("%d files uploaded!", len(files)),
})
})
~~~
这次只有核心代码,可以结合前面单文件稍加修改,这并不难!
## 重定向
### HTTP重定向
HTTP 重定向很容易。 内部、外部重定向均支持。
~~~
r.GET("/test", func(c *gin.Context) {
c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")
})
~~~
### 路由重定向
路由重定向,使用`HandleContext`:
~~~
r.GET("/test", func(c *gin.Context) {
// 指定重定向的URL
c.Request.URL.Path = "/test2"
r.HandleContext(c)
})
r.GET("/test2", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"hello": "world"})
})
~~~
## 中间件
Gin框架允许开发者在处理请求的过程中,加入用户自己的钩子(Hook)函数。这个钩子函数就叫中间件,中间件适合处理一些公共的业务逻辑,比如登录认证、权限校验、数据分页、记录日志、耗时统计等。
### 定义中间件
Gin中的中间件必须是一个`gin.HandlerFunc`类型。例如我们像下面的代码一样定义一个统计请求耗时的中间件。
~~~
// StatCost 是一个统计耗时请求耗时的中间件
func StatCost() gin.HandlerFunc {
return func(c *gin.Context) {
start := time.Now()
c.Set("name", "小王子") // 可以通过c.Set在请求上下文中设置值,后续的处理函数能够取到该值
// 调用该请求的剩余处理程序
c.Next()
// 不调用该请求的剩余处理程序
// c.Abort()
// 计算耗时
cost := time.Since(start)
log.Println(cost)
}
}
~~~
### 注册中间件
~~~
func main() {
// 新建一个没有任何默认中间件的路由
r := gin.New()
// 注册一个全局中间件
r.Use(StatCost())
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()
}
~~~
### 为某个路由单独注册
~~~
// 给/test2路由单独注册中间件(可注册多个)
r.GET("/test2", StatCost(), func(c *gin.Context) {
name := c.MustGet("name").(string) // 从上下文取值
log.Println(name)
c.JSON(http.StatusOK, gin.H{
"message": "Hello world!",
})
})
~~~
### 为路由组注册中间件
写法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) {...})
...
}
~~~
# Gorm框架
## 安装
~~~
go get -u github.com/jinzhu/gorm
~~~
## 连接MySQL
~~~
import (
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
func main() {
db, err := gorm.Open("mysql", "user:password@(localhost)/dbname?charset=utf8mb4&parseTime=True&loc=Local")
defer db.Close()
}
~~~
## GORM操作MySQL
### 创建数据库
~~~
CREATE DATABASE db1;
CREATE TABLE `user_info` (
`id` int(30) NOT NULL AUTO_INCREMENT COMMENT 'id',
`name` varchar(255) DEFAULT NULL,
`gender` varchar(255) DEFAULT NULL COMMENT 'Gender ',
`hobby` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8mb4;
~~~
~~~
表与实体映射的问题搞了我很久,我今天第一次建表的时候字段的首字母都是大写,为了和实体名保持一致。结果反而让自己查不到数据,一直以为是代码的问题,直到把一个配置去了之后,go自动建表对比了下字段名才发现问题!
~~~
### 基本操作
~~~
package main
import (
"fmt"
"github.com/jinzhu/gorm"
_ "github.com/jinzhu/gorm/dialects/mysql"
)
// UserInfo 用户信息
type UserInfo struct {
ID int
Name string
Gender string
Hobby string
}
func main() {
db, err := gorm.Open("mysql", "root:123456@(127.0.0.1:3306)/db1?charset=utf8mb4&parseTime=True&loc=Local")
if err!= nil{
panic(err)
}
defer db.Close()
// 自动迁移
db.AutoMigrate(&UserInfo{})
u1 := UserInfo{1, "七米", "男", "篮球"}
u2 := UserInfo{2, "沙河娜扎", "女", "足球"}
// 创建记录
db.Create(&u1)
db.Create(&u2)
// 查询
var u = new(UserInfo)
db.First(u)
fmt.Printf("%#v\n", u)
var uu UserInfo
db.Find(&uu, "hobby=?", "足球")
fmt.Printf("%#v\n", uu)
// 更新
db.Model(&u).Update("hobby", "双色球")
// 删除
db.Delete(&u)
}
~~~