网站Logo 青山黛

gin响应

1397628709
3
2025-08-08

gin提供了非常多的响应方法

例如 字符串、json、html等

json响应

现在大部分的前后端交互都是以json为主,所以gin中最常用的就是json响应

它的用法非常简单

c.JSON(200, gin.H{
  "code": 0,
  "msg": "ok",
})

但是我们都会对其进行一番封装,例如标准响应格式 code,data,msg

前端可以判断code的值来确定操作是否成功

不过code的定义就是每家公司都有不同的定义了

我比较习惯于code=0为操作成功的状态码,非0值就是具体的错误码,这样可以方便定位错误

例如 code=1001 是权限错误,code=1002 是资源不存在等

如何封装?

package res

import "github.com/gin-gonic/gin"

type Response struct {
  Code int    `json:"code"`
  Data any    `json:"data"`
  Msg  string `json:"msg"`
}

type Code int

const (
  RoleErrCode    Code = 1001
  NetworkErrCode Code = 1002
)

var codeMap = map[Code]string{
  RoleErrCode:    "权限错误",
  NetworkErrCode: "网络错误",
}

func init() {
  // 可能是一个
}

func response(c *gin.Context, r Response) {
  c.JSON(200, r)
}

func Ok(c *gin.Context, data any, msg string) {
  response(c, Response{
    Code: 0,
    Data: data,
    Msg:  msg,
  })
}

func OkWithData(c *gin.Context, data any) {
  Ok(c, data, "成功")
}

func OkWithMsg(c *gin.Context, msg string) {
  Ok(c, map[string]any{}, msg)
}

func Fail(c *gin.Context, code int, data any, msg string) {
  response(c, Response{
    Code: code,
    Data: data,
    Msg:  msg,
  })
}
func FailWithMsg(c *gin.Context, msg string) {
  response(c, Response{
    Code: 7,
    Data: nil,
    Msg:  msg,
  })
}
func FailWithCode(c *gin.Context, code Code) {
  // 去找code对应的msg
  msg, ok := codeMap[code]
  if !ok {
    msg = "未知错误"
  }
  response(c, Response{
    Code: int(code),
    Data: nil,
    Msg:  msg,
  })
}

封装之后,使用就很简单了

res.OkWithMsg(c, "登陆成功")
res.OkWithData(c, map[string]any{
      "name": "枫枫",
    })
res.FailWithMsg(c, "参数错误")

html响应

使用LoadHTMLGlob加载一个目录下的所有html文件

也可以使用LoadHTMLFiles加载单个html文件

load之后,下面才能用这个文件名

package main

import "github.com/gin-gonic/gin"

func main() {
  r := gin.Default()
  // 加载模板 只有这里加载了模板,下面才能用
  r.LoadHTMLGlob("templates/*")
  //r.LoadHTMLFiles("templates/index.html")
  r.GET("", func(c *gin.Context) {
    c.HTML(200, "index.html", nil)
  })
  r.Run(":8080")
}

HTML的第三个参数是可以向HTML中传递数据

但是现在都是前后端分离的时代了,也很少使用后端返回模板了,知道怎么用就好

c.HTML(200, "index.html", map[string]any{
    "title": "这是网页标题",
  })

html文件中使用

<title>{{.title}}</title>

其实借助这个功能,可以很方便的修改网站的标题和logo

简单的扩展一下

关于部署:

  1. 前端单独部署,后端单独部署

  2. 前端打包之后,后端统一部署

响应文件

用于浏览器直接请求这个接口唤起下载

c.Header("Content-Type", "application/octet-stream")              // 表示是文件流,唤起浏览器下载,一般设置了这个,就要设置文件名
c.Header("Content-Disposition", "attachment; filename=3.文件下载.go") // 用来指定下载下来的文件名
c.File("3.文件下载.go")
  1. 要设置Content-Type,唤起浏览器下载

  2. 只能是get请求

前端请求后端接口,然后唤起浏览器下载

c.Header("fileName", "xxx.png")
c.Header("msg", "文件下载成功")
c.File("uploads/12.png")

前端唤起浏览器下载的本质

<a href="文件地址" download="文件名">文件下载</a>
async downloadFile(row) {
   this.$http({
      method: 'post',
      url: 'file/upload',
      data:postData,
      responseType: "blob"
   }).then(res => {
      const _res = res.data
      let blob = new Blob([_res], {
            type: 'application/png'
          });
      let downloadElement = document.createElement("a");
      let href = window.URL.createObjectURL(blob); //创建下载的链接
      downloadElement.href = href;
      downloadElement.download = res.headers["fileName"]; //下载后文件名
      document.body.appendChild(downloadElement);
      downloadElement.click(); //点击下载
      document.body.removeChild(downloadElement); //下载完成移除元素
      window.URL.revokeObjectURL(href); //释放掉blob对象
    })}

最好的做法

调下载接口的请求,后端不返回实际文件内容,而是生成一个临时下载地址

前端构造a标签,再请求这个接口唤起浏览器下载

静态文件

r.Static("static", "static") // 第一个参数是别名,第二个才是实际路径
r.StaticFile("abcd", "static/abc.txt")

静态文件的路径,不能再被路由使用了

动物装饰