第一个Go Web 程序
package mainimport ("fmt""net/http")func hello(w http.ResponseWriter, r *http.Request) {fmt.Fprintf(w, "hello world")}func main() {server := &http.Server{Addr: "127.0.0.1:8080",}http.HandleFunc("/", hello)server.ListenAndServe()}
跑起来的效果:
Gin框架的使用
gin示例
package mainimport ("github.com/gin-gonic/gin""net/http")func main() {//创建一个默认的路由引擎r := gin.Default()// GET:请求方式;/hello:请求的路径// 当客户端以GET方法请求/hello路径时,会执行后面的匿名函数r.GET("/hello", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"message": "hello go",})})r.Run()}
RESTful API(后面会用go实现)
REST与技术无关,代表的是一种软件架构风格,REST是Representational State Transfer的简称,中文翻译为“表征状态转移”或“表现层状态转化”。
简单来说,REST的含义就是客户端与Web服务器之间进行交互的时候,使用HTTP协议中的4个请求方法代表不同的动作。
只要API程序遵循了REST风格,那就可以称其为RESTful API。目前在前后端分离的架构中,前后端基本都是通过RESTful API来进行交互。
Gin框架支持RESTful API的开发。
示例:
package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "GET",})})r.POST("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "POST",})})r.DELETE("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "DELETE",})})r.PUT("/book", func(c *gin.Context) {c.JSON(200, gin.H{"message": "PUT",})})r.Run()}
搭配Postman使用
template 初识
在下面的代码片段里面涉及到了 如何 自定义模版函数 加载静态模版 的方法:
package mainimport ( "github.com/gin-gonic/gin" "html/template" "net/http")//自定义模版函数func main() { r := gin.Default() //加载静态模板 r.Static("/xxx", "./statics") //自定义模版函数 r.SetFuncMap(template.FuncMap{ "safe": func(str string) template.HTML { return template.HTML(str) }, }) //加载模版 r.LoadHTMLGlob("templates/*") r.GET("/index", func(c *gin.Context) { c.HTML(http.StatusOK, "index.html", "<a href='https://yoboot.github.io'>小王的bolg</a>") }) r.Run(":9090")}
//文件路径:templates/*<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title></head><body><div>{{ . | safe }}</div></body></html>
JSON渲染
package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { r := gin.Default() //1gin.H 是map[string]interface{}的缩写 r.GET("/index", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{"小王": 18, "牛魔王": "我卡拉卡"}) }) type msg struct { Name string `json:"name"` Age int `json:"age"` } r.GET("/msg", func(c *gin.Context) { data := msg{ Age: 18, Name: "小王子", } //2.使用结构体 c.JSON(http.StatusOK, data) }) r.Run()}
获取参数
获取querystring参数
querystring 指的是URL中?后面携带的参数,例如:
/user/search?username=小王子&address=沙河。
获取请求的querystring参数的方法如下:
package mainimport ( "github.com/gin-gonic/gin" "net/http")func main() { //querystring r := gin.Default() r.GET("/web", func(c *gin.Context) { // http://localhost:8080/web?query=小王 //获取浏览器那边发送请求携带的参数 query string 参数 //name := c.Query("query") //通过Query获取请求中携带的querystring参数 //name, ok := c.GetQuery("query") //通过GetQuery获取参数,获取不到第二个参数返回bool值 //if !ok { ////获取不到 //name = "sombody" //} name := c.DefaultQuery("query", "sombody") //通过DefaultQuery获取参数,获取不到返回默认值 c.JSON(http.StatusOK, name) }) r.Run()}
获取form 参数
login.html
<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title></head><body><form action="/login" method="post" novalidate autocomplete="off"> <div> <label for="username">username:</label> <input type="text" name="username" id="username"> </div> <div> <label for="password">password</label> <input type="password" name="password" id="password"> </div> <div> <input type="submit" value="登录"> </div></form></body></html>
index.html
<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title></head><body><h1>Hello,{{.Name}}</h1><p>你的密码是:{{.Password}}</p></body></html>
package mainimport ( "github.com/gin-gonic/gin" "net/http")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 post r.POST("/login", func(c *gin.Context) { //第一种获取form表单提交数据的方法 PostForm /*username := c.PostForm("username") password := c.PostForm("password") */ //第二种获取form表单提交数据的方法 DefaultPostForm // DefaultPostForm 找不到数据返回默认值,值得注意的是这里说的是你提交的参数找不到 /*username := c.DefaultPostForm("username", "somebody") password := c.DefaultPostForm("password", "0000") */ //第三种获取方法 GetPostForm 获取不到则返回bool值 username, ok := c.GetPostForm("username") if !ok { username = "somebody" } password, ok := c.GetPostForm("password") if !ok { password = "0000" } c.HTML(200, "index.html", gin.H{ "Name": username, "Password": password, }) }) r.Run()}
获取URL路径参数
http://localhost:8080/user/小王/庆阳
package mainimport "github.com/gin-gonic/gin"func main() {r := gin.Default()r.GET("/user/:username/:address", func(c *gin.Context) {// Param 获取path参数username := c.Param("username")address := c.Param("address")c.JSON(200, gin.H{"message": "ok","username": username,"address": address,})})r.Run()}
执行结果:
{“address”:“庆阳”,“message”:“ok”,“username”:“小王”}
参数绑定
为了能够更方便的获取请求相关参数,提高开发效率,我们可以基于请求的Content-Type识别请求数据类型并利用反射机制自动提取请求中QueryString、form表单、JSON、XML等参数到结构体中。 下面的示例代码演示了.ShouldBind()强大的功能,它能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象。
inde.html:
<!doctype html><html lang="en"><head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>index</title></head><body><form action="/form" method="post"> 用户名: <input type="text" name="username"> 密码: <input type="password" name="password"> <input type="submit" value="提交"></form></body></html>
package mainimport ( "fmt" "github.com/gin-gonic/gin" "net/http")type UserInfo struct { Username string `form:"username"` Password string `form:"password"`}func main() { r := gin.Default() r.LoadHTMLFiles("./index.html") r.GET("/user", func(c *gin.Context) { u := UserInfo{} /*u.Username = c.Query("username") u.Password = c.Query("password")*/ //与上面相比ShouldBind()展示了它的强大之处 err := c.ShouldBind(&u) if err != nil { c.JSON(http.StatusBadGateway, gin.H{ "err": err.Error(), }) } else { fmt.Println(u) c.JSON(200, gin.H{ "message": "ok", }) } }) r.GET("/index", func(c *gin.Context) { c.HTML(200, "index.html", nil) }) r.POST("/form", func(c *gin.Context) { u := UserInfo{} err := c.ShouldBind(&u) if err != nil { c.JSON(http.StatusBadGateway, gin.H{ "err": err.Error(), }) } else { fmt.Println(u) c.JSON(200, gin.H{ "message": "ok", }) } }) //与postman搭配 r.POST("/json", func(c *gin.Context) { u := UserInfo{} err := c.ShouldBind(&u) if err != nil { c.JSON(http.StatusBadGateway, gin.H{ "err": err.Error(), }) } else { fmt.Println(u) c.JSON(200, gin.H{ "message": "ok", }) } }) r.Run()}
文件上传
通过网络将本地文件从客户端上传到服务端
单个文件上传
前端代码:
<!DOCTYPE html><html lang="zh-CN"><head> <title>上传文件示例</title></head><body><form action="/upload" method="post" enctype="multipart/form-data"> <input type="file" name="f1"> <input type="submit" value="上传"></form></body></html>
后端gin代码
package mainimport ( "github.com/gin-gonic/gin" "net/http" "path")func main() { r := gin.Default() r.LoadHTMLFiles("./index.html") r.GET("/index", func(c *gin.Context) { c.HTML(200, "index.html", nil) }) r.POST("/upload", func(c *gin.Context) { //从请求中读取文件 f, err := c.FormFile("f1") if err != nil { c.JSON(http.StatusBadGateway, gin.H{ "error": err.Error(), }) } else { //将读取到的文件保存到本地(服务器) //dst := fmt.Sprint("./%s", f.Filename) dst := path.Join("./", f.Filename) //上传文件到指定目录 c.SaveUploadedFile(f, dst) c.JSON(200, gin.H{ "message": "ok", }) } }) r.Run()}
多个文件上传
func main() {router := gin.Default()// 处理multipart forms提交文件时默认的内存限制是32 MiB// 可以通过下面的方式修改// router.MaxMultipartMemory = 8 << 20 // 8 MiBrouter.POST("/upload", func(c *gin.Context) {// Multipart formform, _ := c.MultipartForm()files := form.File["file"]for index, file := range files {log.Println(file.Filename)dst := fmt.Sprintf("C:/tmp/%s_%d", file.Filename, index)// 上传文件到指定的目录c.SaveUploadedFile(file, dst)}c.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%d files uploaded!", len(files)),})})router.Run()}
HTTP重定向
r.GET("/test", func(c *gin.Context) {c.Redirect(http.StatusMovedPermanently, "http://www.sogo.com/")})
路由重定向
路由重定向,使用HandleContext:
package mainimport ("github.com/gin-gonic/gin""net/http")func main() {r := gin.Default()r.GET("/a", func(c *gin.Context) {c.Request.URL.Path = "/b"r.HandleContext(c)})r.GET("/b", func(c *gin.Context) {c.JSON(200, gin.H{"message": "Yoboot",})})r.Run()}
路由组
这里先介绍两个路由 Any 和 NoRoute
一个可以匹配所有请求方法的Any:
r.Any("./index", func(c *gin.Context) { switch c.Request.Method { case http.MethodGet: c.JSON(200, gin.H{"message": "index get"}) case http.MethodPut: c.JSON(200, gin.H{"message": "index put"}) case http.MethodPost: c.JSON(200, gin.H{"message": "index post"}) } }) ```NoRoute 为没有配置处理函数的路由添加处理程序,默认情况下它返回404代码```go//NoRoute 没有定义的路由 r.NoRoute(func(c *gin.Context) { c.JSON(http.StatusNotFound, gin.H{"msg": "此路由没有定义"}) })
路由组
//把公用的前缀提取出来,创建一个路由组videoGroup := r.Group("/video"){videoGroup.GET("/index", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"msg": "/video/index"})})videoGroup.GET("/xx", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"msg": "/video/xx"})})videoGroup.GET("/oo", func(c *gin.Context) {c.JSON(http.StatusOK, gin.H{"msg": "/video/oo"})})}