zl程序教程

您现在的位置是:首页 >  其他

当前栏目

golang学习之gin(四):参数绑定、文件上传、其他数据格式输出、自定义HTTP配置

2023-09-27 14:29:29 时间

一、参数绑定:

1. 什么是参数绑定:

能够基于请求自动提取JSON、form表单和QueryString类型的数据,并把值绑定到指定的结构体对象

2. ShouldBind:

./
├── chapter04
│   └── post.go
├── main.go
│   ├── css
│   │   └── index.css
│   ├── images
│   └── js
└── template
    ├── chapter04
    │   └── user_add.html
  • ./main.go
package main

import (
	"gin_project/chapter04"
	"github.com/gin-gonic/gin"
)

func main() {
	engine := gin.Default()
	// 注册模板
	engine.LoadHTMLGlob("template/**/*")
	// 注册静态文件
	engine.Static("./static", "static")

	// 注册路由
	// POST
	engine.GET("/to_user_add", chapter04.ToUserAdd4) // 获取请求页面
	engine.POST("/user_add", chapter04.PostForm4)

	engine.Run(":9000")
}
  • ./chapter04/post.go
package chapter04

import (
	"fmt"
	"net/http"

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

type User struct {
	// structTag:指定字段名称,不用使用首字母大写的
	Id      int    `form:"id" json:"id"`
	Name    string `form:"username" json:"username`
	Age     string `form:"age" json:"age`
	Address string `form:"address" json:"address"`
}

// post
func ToUserAdd4(ctx *gin.Context) {
	ctx.HTML(http.StatusOK, "chapter04/user_add.html", nil)
}

func PostForm4(ctx *gin.Context) {
	var userInfo User
	err := ctx.ShouldBind(&userInfo)
	fmt.Println(err)      //<nil>
	fmt.Println(userInfo) // {0 user 12 西三旗}
	ctx.String(http.StatusOK, "hello")
}
  • ./template/chapter04/user_add.html
{{ define "chapter04/user_add.html" }}
<!DOCTYPE html>
<html lang="zh">
<head>
    <title>post请求练习</title>
    <style>
        .userForm {
            width: 480px;
            height: 360px;
            margin: 20px 200px;
        }
        input {
            margin: 5px 0;
        }
    </style>
</head>
<body>
    <div class="userForm">
        <h2>添加用户</h2>
        <form action="/user_add" method="post">
            <span>用户名: </span><input type="text" name="username"><br>
            <span>&emsp;龄: </span><input type="text" name=" "><br>
            <span>&emsp;址: </span><input type="text" name="address"><br>
            <input type="submit" value="提交">
        </form>
    </div>
</body>
</html>
{{ end }}

3. ShouldBindWith

可以使用显式绑定声明绑定 multipart form:

c.ShouldBindWith(&form, binding.Form)

或者简单地使用 ShouldBind 方法自动绑定

4. ShouldBindQuery

ShouldBindQuery函数只绑定 url 查询参数而忽略 post 数据

二、文件上传:

1. form表单上传单文件&&多文件:

.
├── chapter04
│   ├── file_upload.go
│   └── post.go
├── main.go
├── static
│   ├── css
│   ├── images
│   └── js
├── template
│   ├── chapter04
│   │   ├── fileUpload.html
│   │   └── user_add.html
└── upload
    ├── timg.jpeg
    ├── ttt.jpg
    └── �\217\220示�\237�2.mp3
  • ./main.go
package main

import (
	"gin_project/chapter04"
	"github.com/gin-gonic/gin"
)

func main() {
	engine := gin.Default()
	// 注册模板
	engine.LoadHTMLGlob("template/**/*")
	// 注册静态文件
	engine.Static("./static", "static")

	// 注册路由
	// 文件上传
	engine.GET("/to_fileUpload", chapter04.ToFileUpload)
	engine.POST("/fileUpload", chapter04.FileUpload)   // 表单单文件
	engine.POST("/fileUploads", chapter04.FileUploads) // 表单多文件

	engine.Run(":9000")
}
  • ./chapter04/file_upload.go
package chapter04

import (
	"fmt"
	"net/http"
	"strconv"
	"time"
	"github.com/gin-gonic/gin"
)

func ToFileUpload(ctx *gin.Context) {
	ctx.HTML(http.StatusOK, "chapter04/fileUpload.html", nil)
}
// 接收单文件
func FileUpload(ctx *gin.Context) {
	file, _ := ctx.FormFile("file") // 获取文件
	fmt.Println(file.Filename)
	time_unix := strconv.FormatInt(time.Now().Unix(), 10) // 获取时间戳并转成字符串
	file_path := "upload/" + time_unix + file.Filename    // 设置保存文件的路径,不要忘了后面的文件名
	ctx.SaveUploadedFile(file, file_path)                 // 保存文件
	ctx.String(http.StatusOK, "上传成功")
}
// 接收多文件
func FileUploads(ctx *gin.Context) {
	form, _ := ctx.MultipartForm()
	files := form.File["file"] // 获取文件

	for _, file := range files {
		fmt.Println(file.Filename)
		file_path := "upload/" + file.Filename // 设置保存文件的路径,不要忘了后面的文件名
		ctx.SaveUploadedFile(file, file_path)  // 保存文件
	}

	ctx.String(http.StatusOK, "上传成功")
}
  • ./template/chapter04/fileUpload.html
{{ define "chapter04/fileUpload.html" }}
<!DOCTYPE html>
<html lang="zh">
<head>
    <title>文件上传练习</title>
    <style>
        .fileForm {
            width: 300px;
            margin: 100px auto;
        }
    </style>
</head>
<body>
    <div class="fileForm">
        <h2>上传单文件</h2>
        <form action="/fileUpload" method="post" enctype="multipart/form-data">
            <!-- 注意:设置enctype参数 -->
            <span>文件: </span><input type="file" name="file"><br>
            <input type="submit" value="提交">
        </form>
    </div>

    <div class="fileForm">
        <h2>上传多文件</h2>
        <form action="/fileUploads" method="post" enctype="multipart/form-data">
        	<!-- 相同的name -->
            <span>文件1: </span><input type="file" name="file"><br>
            <span>文件2: </span><input type="file" name="file"><br>
            <span>文件3: </span><input type="file" name="file"><br>
            <input type="submit" value="提交">
        </form>
    </div>
</body>
</html>
{{ end }}

2. ajax上传单文件&&多文件:

./
├── chapter04
│   ├── file_upload.go
├── main.go
├── static
│   ├── css
│   ├── images
│   └── js
├── template
│   ├── chapter04
│   │   ├── ajaxFile.html
│   │   ├── fileUpload.html
└── upload
  • ./main.go
package main

import (
	"gin_project/chapter04"
	"github.com/gin-gonic/gin"
)
func main() {
	engine := gin.Default()
	// 注册模板
	engine.LoadHTMLGlob("template/**/*")
	// 注册静态文件
	engine.Static("./static", "static")

	// 注册路由
	engine.GET("/to_user_add", chapter04.ToUserAdd4) // 获取请求页面
	engine.POST("/user_add", chapter04.PostForm4)

	// 文件上传
	engine.GET("/to_ajaxFileUpload", chapter04.ToAjaxFileUpload)
	engine.POST("/ajaxFileUpload", chapter04.AjaxFileUpload)   // ajax单文件
	engine.POST("/ajaxFileUploads", chapter04.AjaxFileUploads) // ajax多文件

	engine.Run(":9000")
}
  • ./chapter04/file_upload.go
package chapter04

import (
	"fmt"
	"net/http"
	"strconv"
	"time"
	"github.com/gin-gonic/gin"
)

func ToAjaxFileUpload(ctx *gin.Context) {
	ctx.HTML(http.StatusOK, "chapter04/ajaxFile.html", nil)
}

func AjaxFileUpload(ctx *gin.Context) {
	file, _ := ctx.FormFile("file") // 获取文件
	fmt.Println(file.Filename)
	time_unix := strconv.FormatInt(time.Now().Unix(), 10) // 获取时间戳并转成字符串
	file_path := "upload/" + time_unix + file.Filename    // 设置保存文件的路径,不要忘了后面的文件名
	ctx.SaveUploadedFile(file, file_path)                 // 保存文件
	ctx.String(http.StatusOK, "上传成功")
}

func AjaxFileUploads(ctx *gin.Context) {
	form, _ := ctx.MultipartForm()
	files := form.File["file"] // 获取文件

	for _, file := range files {
		fmt.Println(file.Filename)
		file_path := "upload/" + file.Filename // 设置保存文件的路径,不要忘了后面的文件名
		ctx.SaveUploadedFile(file, file_path)  // 保存文件
	}

	ctx.String(http.StatusOK, "上传成功")
}
  • ./template/chapter04/ajaxFile.html
{{ define "chapter04/ajaxFile.html" }}
<!DOCTYPE html>
<html lang="zh">
<head>
    <title>ajax文件上传练习</title>
    <script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
    <style>
        .fileForm {
            width: 300px;
            margin: 100px auto;
        }
    </style>
</head>
<body>
    <div class="fileForm">
        <h2>ajax上传单文件</h2>
        <form>
            <span>文件: </span><input type="file" name="file" id="file"><br>
            <input type="button" id="file_btn" value="提交">
        </form>
    </div>

    <div class="fileForm">
        <h2>ajax上传多文件</h2>
        <form>
            <!-- 相同的name -->
            <span>文件1: </span><input type="file" name="files" class="files"><br>
            <span>文件2: </span><input type="file" name="files" class="files"><br>
            <span>文件3: </span><input type="file" name="files" class="files"><br>
            <input type="button" value="提交" id="files_btn">
        </form>
    </div>

    <script>
        // 上传单个文件
        var file_btn = document.getElementById("file_btn");
        file_btn.onclick = function (ev) {
            var file = $("#file")[0].files[0];
            var form_data = new FormData();
            form_data.append("file",file);
            // ajax中需要加两个参数:
            //     contentType:false,
            //     processData:false,
            $.ajax({
                url:"/ajaxFileUpload",
                type:"POST",
                data:form_data,
                contentType:false,
                processData:false,
                success:function (data) {
                    alert(data);
                },
                fail:function (data) {
                    console.log(data);
                }
            })
        }

        // 上传多个文件
        var files_btn = document.querySelector("#files_btn")
        files_btn.onclick = function(ev) {
            var files = document.querySelectorAll(".files")
            console.log(files);

            var form_data = new FormData();
            for(let file in files) {
                form_data.append("file",file);
            }
            $.ajax({
                url:"/ajaxFileUploads",
                type:"POST",
                data:form_data,
                contentType:false,
                processData:false,
                success:function (data) {
                    alert(data);
                },
                fail:function (data) {
                    console.log(data);
                }
            })
        }
    </script>
</body>
</html>
{{ end }}

三、其他数据格式输出:

1. JSON:

func OutJson(ctx *gin.Context) {
	ctx.JSON(http.StatusOK, gin.H{
		"code": 200,
		"tag":  "<br>",
		"msg":  "提交成功",
		"html": "<b>Hello, world!</b>",
	})
	// {"code":200,"html":"\u003cb\u003eHello, world!\u003c/b\u003e","msg":"提交成功","tag":"\u003cbr\u003e"}
}

2. AsciiJSON:

生成具有转义的非 ASCII 字符的 ASCII-only JSON

func OutAsciiJson(ctx *gin.Context) {
	ctx.AsciiJSON(http.StatusOK, gin.H{
		"code": 200,
		"tag":  "<br>",
		"msg":  "提交成功",
		"html": "<b>Hello, world!</b>",
	})
	// {"code":200,"html":"\u003cb\u003eHello, world!\u003c/b\u003e","msg":"\u63d0\u4ea4\u6210\u529f","tag":"\u003cbr\u003e"}
}

3. JSONP:

使用 JSONP 向不同域的服务器请求数据。如果查询参数存在回调,则将回调添加到响应体中;
如果传输的数据在两个不同的域,由于在javascript里无法跨域获取数据,所以一般采取script标签的方式获取数据,传入一些callback来获取最终的数据,这就有可能造成敏感信息被劫持;

func OutJsonp(ctx *gin.Context) {
	ctx.AsciiJSON(http.StatusOK, gin.H{
		"code": 200,
		"tag":  "<br>",
		"msg":  "提交成功",
		"html": "<b>Hello, world!</b>",
	})
	// {"code":200,"html":"\u003cb\u003eHello, world!\u003c/b\u003e","msg":"提交成功","tag":"\u003cbr\u003e"}
}

4. PureJSON:

func OutPureJSON(ctx *gin.Context) {
	ctx.PureJSON(http.StatusOK, gin.H{
		"code": 200,
		"tag":  "<br>",
		"msg":  "提交成功",
	})
	// {"code":200,"html":"<b>Hello, world!</b>","msg":"提交成功","tag":"<br>"}
}

5. SecureJSON

使用 SecureJSON 防止 json 劫持。如果给定的结构是数组值,则默认预置 “while(1),” 到响应体;
json劫持:利用网站的cookie未过期,然后访问了攻击者的虚假页面,那么该页面就可以拿到json形式的用户敏感信息;

func OutSecureJSON(ctx *gin.Context) {
	names := []string{"lena", "austin", "foo"}
	ctx.SecureJSON(http.StatusOK, names)
}

在这里插入图片描述

6. XML:

func OutXML(ctx *gin.Context) {
	ctx.XML(http.StatusOK, gin.H{
		"code": 200,
		"tag":  "<br>",
		"msg":  "提交成功",
		"html": "<b>Hello, world!</b>",
	})
}

在这里插入图片描述

7. YAML:

func OutYML(ctx *gin.Context) {
	ctx.YAML(http.StatusOK, gin.H{
		"code": 200,
		"tag":  "<br>",
		"user": gin.H{"name": "zhiliao", "age": 18},
		"html": "<b>Hello, world!</b>",
	})
}

8.ProtoBuf:

四、自定义HTTP配置:

1.http请求补充:

router := gin.Default()

router.GET("/someGet", getting)
router.POST("/somePost", posting)
router.PUT("/somePut", putting)
router.DELETE("/someDelete", deleting)
router.PATCH("/somePatch", patching)
router.HEAD("/someHead", head)
router.OPTIONS("/someOptions", options)

2. 设置启动参数

func main() {
    router := gin.Default()
    ...
    http.ListenAndServe(":8080", router)
}
func main() {
    router := gin.Default()
	...
	
    s := &http.Server{
        Addr:           ":8080",
        Handler:        router,
        ReadTimeout:    10 * time.Second,
        WriteTimeout:   10 * time.Second,
        MaxHeaderBytes: 1 << 20,
    }
    s.ListenAndServe()
}