└── README.md /README.md: -------------------------------------------------------------------------------- 1 | - [安装与配置](#install) 2 | - [框架架构](#arch) 3 | - [生命周期](#life-circle) 4 | - [Context](#context) 5 | - [路由](#router) 6 | - [基本路由](#basic-router) 7 | - [路由参数](#router-param) 8 | - [路由群组](#router-group) 9 | - [控制器](#controller) 10 | - [请求](#request) 11 | - [请求头](#request-header) 12 | - [Cookies](#request-cookie) 13 | - [上传文件](#upload) 14 | - [响应](#response) 15 | - [响应头](#response-header) 16 | - [附加Cookie](#response-cookie) 17 | - [字符串响应](#response-string) 18 | - [JSON响应](#response-json) 19 | - [视图响应](#response-view) 20 | - [文件下载](#response-file) 21 | - [重定向](#response-redirect) 22 | - [同步异步](#sync-async) 23 | - [视图](#view) 24 | - [传参](#view-param) 25 | - [视图组件](#view-unit) 26 | - [中间件](#middleware) 27 | - [分类使用](#middleware-use) 28 | - [创建中间件](#middleware-create) 29 | - [中间件参数](#middleware-param) 30 | - [数据库](#db) 31 | - [Mongodb](#db-mongodb) 32 | - [Mysql](#db-mysql) 33 | - [ORM](#orm) 34 | - [扩展包](#extensions) 35 | - [常用方法](#functions) 36 | - [gin]() 37 | - [Context]() 38 | 39 | 40 | ### 安装与配置 41 | 安装: 42 | 43 | ```sh 44 | $ go get gopkg.in/gin-gonic/gin.v1 45 | ``` 46 | ` 47 | 注意:确保 GOPATH GOROOT 已经配置 48 | ` 49 | 50 | 导入: 51 | ```go 52 | import "gopkg.in/gin-gonic/gin.v1" 53 | ``` 54 | 55 | 56 | 57 | 58 | ### 框架架构 59 | 60 | 61 | - HTTP 服务器 62 | 63 | **1.默认服务器** 64 | 65 | ``` 66 | router.Run() 67 | ``` 68 | 69 | **2.HTTP 服务器** 70 | 71 | 除了默认服务器中 `router.Run()` 的方式外,还可以用 `http.ListenAndServe()`,比如 72 | 73 | ```go 74 | func main() { 75 | router := gin.Default() 76 | http.ListenAndServe(":8080", router) 77 | } 78 | ``` 79 | 或者自定义 HTTP 服务器的配置: 80 | 81 | ```go 82 | func main() { 83 | router := gin.Default() 84 | 85 | s := &http.Server{ 86 | Addr: ":8080", 87 | Handler: router, 88 | ReadTimeout: 10 * time.Second, 89 | WriteTimeout: 10 * time.Second, 90 | MaxHeaderBytes: 1 << 20, 91 | } 92 | s.ListenAndServe() 93 | } 94 | ``` 95 | 96 | **3.HTTP 服务器替换方案** 97 | 想无缝重启、停机吗? 以下有几种方式: 98 | 99 | 我们可以使用 [fvbock/endless](https://github.com/fvbock/endless) 来替换默认的 `ListenAndServe`。但是 windows 不能使用。 100 | 101 | ```go 102 | router := gin.Default() 103 | router.GET("/", handler) 104 | // [...] 105 | endless.ListenAndServe(":4242", router) 106 | ``` 107 | 108 | 除了 endless 还可以用manners: 109 | 110 | [manners](https://github.com/braintree/manners) 兼容windows 111 | 112 | ``` 113 | manners.ListenAndServe(":8888", r) 114 | ``` 115 | 116 | 如果你使用的 golang 版本大于 1.8 版本, 那么可以用 http.Server 内置的 Shutdown 方法来实现优雅的关闭服务, 一个简单的示例代码如下: 117 | 118 | ``` 119 | srv := http.Server{ 120 | Addr: ":8080", 121 | Handler: router, 122 | } 123 | 124 | go func() { 125 | if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { 126 | log.Fatalf("listen: %s\n", err) 127 | } 128 | } 129 | 130 | // 其他代码, 等待关闭信号 131 | ... 132 | 133 | ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) 134 | defer cancel() 135 | if err := srv.Shutdown(ctx); err != nil { 136 | log.Fatal("Server Shutdown: ", err) 137 | } 138 | log.Println("Server exiting") 139 | ``` 140 | 141 | 完整的代码见 [graceful-shutdown](https://github.com/gin-gonic/gin/blob/master/examples/graceful-shutdown/graceful-shutdown/server.go). 142 | 143 | 144 | 145 | - 生命周期 146 | 147 | 148 | 149 | - Context 150 | 151 | 152 | 153 | ### 路由 154 | 155 | 156 | - 基本路由 157 | gin 框架中采用的路由库是 httprouter。 158 | 159 | 160 | ```go 161 | // 创建带有默认中间件的路由: 162 | // 日志与恢复中间件 163 | router := gin.Default() 164 | //创建不带中间件的路由: 165 | //r := gin.New() 166 | 167 | router.GET("/someGet", getting) 168 | router.POST("/somePost", posting) 169 | router.PUT("/somePut", putting) 170 | router.DELETE("/someDelete", deleting) 171 | router.PATCH("/somePatch", patching) 172 | router.HEAD("/someHead", head) 173 | router.OPTIONS("/someOptions", options) 174 | ``` 175 | 176 | 177 | 178 | - 路由参数 179 | 180 | api 参数通过Context的Param方法来获取 181 | 182 | ``` 183 | router.GET("/string/:name", func(c *gin.Context) { 184 | name := c.Param("name") 185 | fmt.Println("Hello %s", name) 186 | }) 187 | ``` 188 | 189 | URL 参数通过 DefaultQuery 或 Query 方法获取 190 | 191 | ``` 192 | // url 为 http://localhost:8080/welcome?name=ningskyer时 193 | // 输出 Hello ningskyer 194 | // url 为 http://localhost:8080/welcome时 195 | // 输出 Hello Guest 196 | router.GET("/welcome", func(c *gin.Context) { 197 | name := c.DefaultQuery("name", "Guest") //可设置默认值 198 | // 是 c.Request.URL.Query().Get("lastname") 的简写 199 | lastname := c.Query("lastname") 200 | fmt.Println("Hello %s", name) 201 | }) 202 | ``` 203 | 表单参数通过 PostForm 方法获取 204 | ``` 205 | //form 206 | router.POST("/form", func(c *gin.Context) { 207 | type := c.DefaultPostForm("type", "alert")//可设置默认值 208 | msg := c.PostForm("msg") 209 | title := c.PostForm("title") 210 | fmt.Println("type is %s, msg is %s, title is %s", type, msg, title) 211 | }) 212 | ``` 213 | 214 | 215 | - 路由群组 216 | 217 | ```go 218 | someGroup := router.Group("/someGroup") 219 | { 220 | someGroup.GET("/someGet", getting) 221 | someGroup.POST("/somePost", posting) 222 | } 223 | ``` 224 | 225 | 226 | 227 | ### 控制器 228 | 229 | 230 | 231 | - 数据解析绑定 232 | 233 | 模型绑定可以将请求体绑定给一个类型,目前支持绑定的类型有 JSON, XML 和标准表单数据 (foo=bar&boo=baz)。 234 | 要注意的是绑定时需要给字段设置绑定类型的标签。比如绑定 JSON 数据时,设置 `json:"fieldname"`。 235 | 使用绑定方法时,Gin 会根据请求头中 Content-Type 来自动判断需要解析的类型。如果你明确绑定的类型,你可以不用自动推断,而用 BindWith 方法。 236 | 你也可以指定某字段是必需的。如果一个字段被 `binding:"required"` 修饰而值却是空的,请求会失败并返回错误。 237 | 238 | ```go 239 | // Binding from JSON 240 | type Login struct { 241 | User string `form:"user" json:"user" binding:"required"` 242 | Password string `form:"password" json:"password" binding:"required"` 243 | } 244 | 245 | func main() { 246 | router := gin.Default() 247 | 248 | // 绑定JSON的例子 ({"user": "manu", "password": "123"}) 249 | router.POST("/loginJSON", func(c *gin.Context) { 250 | var json Login 251 | 252 | if c.BindJSON(&json) == nil { 253 | if json.User == "manu" && json.Password == "123" { 254 | c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) 255 | } else { 256 | c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) 257 | } 258 | } 259 | }) 260 | 261 | // 绑定普通表单的例子 (user=manu&password=123) 262 | router.POST("/loginForm", func(c *gin.Context) { 263 | var form Login 264 | // 根据请求头中 content-type 自动推断. 265 | if c.Bind(&form) == nil { 266 | if form.User == "manu" && form.Password == "123" { 267 | c.JSON(http.StatusOK, gin.H{"status": "you are logged in"}) 268 | } else { 269 | c.JSON(http.StatusUnauthorized, gin.H{"status": "unauthorized"}) 270 | } 271 | } 272 | }) 273 | // 绑定多媒体表单的例子 (user=manu&password=123) 274 | router.POST("/login", func(c *gin.Context) { 275 | var form LoginForm 276 | // 你可以显式声明来绑定多媒体表单: 277 | // c.BindWith(&form, binding.Form) 278 | // 或者使用自动推断: 279 | if c.Bind(&form) == nil { 280 | if form.User == "user" && form.Password == "password" { 281 | c.JSON(200, gin.H{"status": "you are logged in"}) 282 | } else { 283 | c.JSON(401, gin.H{"status": "unauthorized"}) 284 | } 285 | } 286 | }) 287 | // Listen and serve on 0.0.0.0:8080 288 | router.Run(":8080") 289 | } 290 | ``` 291 | 292 | 293 | ### 请求 294 | 295 | 296 | - 请求头 297 | 298 | 299 | 300 | - 请求参数 301 | 302 | 303 | 304 | - Cookies 305 | 306 | 307 | 308 | - 上传文件 309 | 310 | ``` 311 | router.POST("/upload", func(c *gin.Context) { 312 | 313 | file, header , err := c.Request.FormFile("upload") 314 | filename := header.Filename 315 | fmt.Println(header.Filename) 316 | out, err := os.Create("./tmp/"+filename+".png") 317 | if err != nil { 318 | log.Fatal(err) 319 | } 320 | defer out.Close() 321 | _, err = io.Copy(out, file) 322 | if err != nil { 323 | log.Fatal(err) 324 | } 325 | }) 326 | ``` 327 | 328 | 329 | ### 响应 330 | 331 | 332 | - 响应头 333 | 334 | 335 | 336 | - 附加Cookie 337 | 338 | 339 | 340 | - 字符串响应 341 | 342 | ``` 343 | c.String(http.StatusOK, "some string") 344 | ``` 345 | 346 | 347 | - JSON/XML/YAML响应 348 | 349 | ``` 350 | r.GET("/moreJSON", func(c *gin.Context) { 351 | // You also can use a struct 352 | var msg struct { 353 | Name string `json:"user" xml:"user"` 354 | Message string 355 | Number int 356 | } 357 | msg.Name = "Lena" 358 | msg.Message = "hey" 359 | msg.Number = 123 360 | // 注意 msg.Name 变成了 "user" 字段 361 | // 以下方式都会输出 : {"user": "Lena", "Message": "hey", "Number": 123} 362 | c.JSON(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123}) 363 | c.XML(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123}) 364 | c.YAML(http.StatusOK, gin.H{"user": "Lena", "Message": "hey", "Number": 123}) 365 | c.JSON(http.StatusOK, msg) 366 | c.XML(http.StatusOK, msg) 367 | c.YAML(http.StatusOK, msg) 368 | }) 369 | 370 | ``` 371 | 372 | 373 | - 视图响应 374 | 375 | 先要使用 LoadHTMLTemplates() 方法来加载模板文件 376 | 377 | ```go 378 | func main() { 379 | router := gin.Default() 380 | //加载模板 381 | router.LoadHTMLGlob("templates/*") 382 | //router.LoadHTMLFiles("templates/template1.html", "templates/template2.html") 383 | //定义路由 384 | router.GET("/index", func(c *gin.Context) { 385 | //根据完整文件名渲染模板,并传递参数 386 | c.HTML(http.StatusOK, "index.tmpl", gin.H{ 387 | "title": "Main website", 388 | }) 389 | }) 390 | router.Run(":8080") 391 | } 392 | ``` 393 | 394 | 模板结构定义 395 | 396 | ```html 397 | 398 |