### 【GoWeb开发实战】Gin框架\_响应
# 响应
既然请求可以使用不同的content-type,响应也如此。通常响应会有html,text,plain,json和xml等。 gin提供了很优雅的渲染方法。
## 一、JSON/XML/YAML渲染
创建一个go文件(demo10\_rendering.go):
~~~go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
"github.com/gin-gonic/gin/testdata/protoexample"
)
func main() {
r := gin.Default()
// gin.H is a shortcut for map[string]interface{}
r.GET("/someJSON", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/moreJSON", func(c *gin.Context) {
// You also can use a struct
var msg struct {
Name string `json:"user"`
Message string
Number int
}
msg.Name = "hanru"
msg.Message = "hey"
msg.Number = 123
// 注意 msg.Name 变成了 "user" 字段
// 以下方式都会输出 : {"user": "hanru", "Message": "hey", "Number": 123}
c.JSON(http.StatusOK, msg)
})
r.GET("/someXML", func(c *gin.Context) {
c.XML(http.StatusOK, gin.H{"user":"hanru","message": "hey", "status": http.StatusOK})
})
r.GET("/someYAML", func(c *gin.Context) {
c.YAML(http.StatusOK, gin.H{"message": "hey", "status": http.StatusOK})
})
r.GET("/someProtoBuf", func(c *gin.Context) {
reps := []int64{int64(1), int64(2)}
label := "test"
// The specific definition of protobuf is written in the testdata/protoexample file.
data := &protoexample.Test{
Label: &label,
Reps: reps,
}
// Note that data becomes binary data in the response
// Will output protoexample.Test protobuf serialized data
c.ProtoBuf(http.StatusOK, data)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
~~~
运行项目,打开浏览器输入网址:[http://127.0.0.1:8080/moreJSON](http://127.0.0.1:8080/moreJSON)

重新输入网址:[http://127.0.0.1:8080/someXML](http://127.0.0.1:8080/someXML)

## 二、HTML模板渲染
gin支持加载HTML模板, 然后根据模板参数进行配置并返回相应的数据。
先要使用 LoadHTMLGlob() 或者 LoadHTMLFiles()方法来加载模板文件,新建一个go文件(demo11\_html.go):
~~~go
func main() {
router := gin.Default()
//加载模板
router.LoadHTMLGlob("templates/*")
//router.LoadHTMLFiles("templates/template1.html", "templates/template2.html")
//定义路由
router.GET("/index", func(c *gin.Context) {
//根据完整文件名渲染模板,并传递参数
c.HTML(http.StatusOK, "index.tmpl", gin.H{
"title": "Main website",
})
})
router.Run(":8080")
}
~~~
创建一个目录:templates,然后在该目录下创建一个模板文件:
templates/index.tmpl
~~~html
<html>
<h1>
{{ .title }}
</h1>
</html>
~~~
运行项目,打开浏览器输入地址:[http://127.0.0.1:8080/index](http://127.0.0.1:8080/index)

不同文件夹下模板名字可以相同,此时需要 LoadHTMLGlob() 加载两层模板路径。
~~~go
router.LoadHTMLGlob("templates/**/*")
router.GET("/posts/index", func(c *gin.Context) {
c.HTML(http.StatusOK, "posts/index.tmpl", gin.H{
"title": "Posts",
})
c.HTML(http.StatusOK, "users/index.tmpl", gin.H{
"title": "Users",
})
})
~~~
重启项目后,打开浏览器输入以下网址:[http://127.0.0.1:8080/posts/index](http://127.0.0.1:8080/posts/index)

gin也可以使用自定义的模板引擎,如下
~~~go
import "html/template"
func main() {
router := gin.Default()
html := template.Must(template.ParseFiles("file1", "file2"))
router.SetHTMLTemplate(html)
router.Run(":8080")
}
~~~
## 三、文件响应
**静态文件服务**
可以向客户端展示本地的一些文件信息,例如显示某路径下地文件。服务端代码是:
~~~go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
router := gin.Default()
// 下面测试静态文件服务
// 显示当前文件夹下的所有文件/或者指定文件
router.StaticFS("/showDir", http.Dir("."))
router.StaticFS("/files", http.Dir("/bin"))
//Static提供给定文件系统根目录中的文件。
//router.Static("/files", "/bin")
router.StaticFile("/image", "./assets/miao.jpg")
router.Run(":8080")
}
~~~
打开浏览器,输入地址:[http://127.0.0.1:8080/showDir,访问当前项目目录的内容](http://127.0.0.1:8080/showDir%EF%BC%8C%E8%AE%BF%E9%97%AE%E5%BD%93%E5%89%8D%E9%A1%B9%E7%9B%AE%E7%9B%AE%E5%BD%95%E7%9A%84%E5%86%85%E5%AE%B9)

重新输入地址:[http://127.0.0.1:8080/files,访问操作系统/bin的下的内容](http://127.0.0.1:8080/files%EF%BC%8C%E8%AE%BF%E9%97%AE%E6%93%8D%E4%BD%9C%E7%B3%BB%E7%BB%9F/bin%E7%9A%84%E4%B8%8B%E7%9A%84%E5%86%85%E5%AE%B9):

浏览器中重新输入地址:[http://127.0.0.1:8080/image](http://127.0.0.1:8080/image)

## 四、重定向
新建一个go文件(demo13\_redirect.go):
~~~go
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
func main() {
r := gin.Default()
r.GET("/redirect", func(c *gin.Context) {
//支持内部和外部的重定向
c.Redirect(http.StatusMovedPermanently, "http://www.baidu.com/")
})
r.Run(":8080")
}
~~~
打开浏览器输入:[http://127.0.0.1:8080/redirect,我们可以看到通过访问的路径,可以重定向到百度地址](http://127.0.0.1:8080/redirect%EF%BC%8C%E6%88%91%E4%BB%AC%E5%8F%AF%E4%BB%A5%E7%9C%8B%E5%88%B0%E9%80%9A%E8%BF%87%E8%AE%BF%E9%97%AE%E7%9A%84%E8%B7%AF%E5%BE%84%EF%BC%8C%E5%8F%AF%E4%BB%A5%E9%87%8D%E5%AE%9A%E5%90%91%E5%88%B0%E7%99%BE%E5%BA%A6%E5%9C%B0%E5%9D%80)。

## 五、同步异步
goroutine 机制可以方便地实现异步处理。当在中间件或处理程序中启动新的Goroutines时,你不应该在原始上下文使用它,你必须使用只读的副本。
新建一个go文件:
~~~go
package main
import (
"time"
"github.com/gin-gonic/gin"
"log"
)
func main() {
r := gin.Default()
//1. 异步
r.GET("/long_async", func(c *gin.Context) {
// goroutine 中只能使用只读的上下文 c.Copy()
cCp := c.Copy()
go func() {
time.Sleep(5 * time.Second)
// 注意使用只读上下文
log.Println("Done! in path " + cCp.Request.URL.Path)
}()
})
//2. 同步
r.GET("/long_sync", func(c *gin.Context) {
time.Sleep(5 * time.Second)
// 注意可以使用原始上下文
log.Println("Done! in path " + c.Request.URL.Path)
})
// Listen and serve on 0.0.0.0:8080
r.Run(":8080")
}
~~~
启动程序,打开浏览器并输入网址:[http://127.0.0.1:8080/long\_sync](http://127.0.0.1:8080/long_sync),

然后在控制台观察打印的结果:

然后我们在浏览器中更改地址:[http://127.0.0.1:8080/long\_async](http://127.0.0.1:8080/long_async),

然后观察控制台中打印的内容:
