gorilla/schema可将前端传来的form表单自动映射到go语言的结构体上。可简化Golang后端对form表单各个属性值的获取,支持的参数类型如下:
bool
float variants (float32, float64)
int variants (int, int8, int16, int32, int64)
string
uint variants (uint, uint8, uint16, uint32, uint64)
struct
a pointer to one of the above types
a slice or a pointer to a slice of one of the above types
其中不支持的类型如文件、图片等可做特殊处理,代码可见示例代码(带文件) 。
gihub地址 : https://github.com/gorilla/schema
go get -t github.com/gorilla/schema
使用 *http.Request 自带的方法进行解析form。
request.ParseMultipartForm(maxMemory)
重要(坑):
当表单格式为application/x-www-form-urlencoded时, 使用 request.ParseForm()
当表单格式为multipart/form-data时, 使用 request.ParseMultipartForm(maxMemory)
maxMemory 参数解释:此参数限制了request请求体存储在内存中的大小,其余部分将存储在硬盘的临时文件中,可根据需要设置为1024的整数倍。
重要(坑):
1)、当结构体不设置tag时,会按照结构体的属性名称和form表单的属性name值映射匹配。如下所示:
type Person struct {
// 此时前端form表单的属性name值也必须为 ID, Phone
ID string
Phone string
}
2)、在结构体属性名称和form表单属性name值不相符时,需要通过设置结构体的tag来映射。gorilla/schema默认检索的tag为:schema,如下代码所示:
type Person struct {
// 此处 tb_name、tb_phone等为前端form表单的各个属性名
Name string `schema:"tb_name"`
Phone string `schema:"tb_phone"`
}
3)、可通过decoder.SetAliasTag(tag)方法自定义需要检索的tag,将tag设置为自己的结构体标签。否则会映射失败,代码如下:
type Person struct {
// 此处 tb_name、tb_phone等为前端form表单的各个属性名
Name string `json:"tb_name"`
Phone string `json:"tb_phone"`
}
var decoder = schema.NewDecoder()
var student Student
decoder.SetAliasTag("json")
decoder.Decode(&student, request.PostForm)
import (
"encoding/json"
"fmt"
"github.com/gorilla/schema"
"io"
"mime/multipart"
"net/http"
"os"
"strconv"
"time"
)
var decoder = schema.NewDecoder()
// 此常量为存储上传文件的文件夹路径
const sourcePath = "D:\\files\\"
type Person struct {
// 此处 tb_name、tb_phone等为前端form表单的各个属性名
Name string `json:"tb_name"`
Phone string `json:"tb_phone"`
}
type Student struct {
Person
Id int `json:"tb_id"`
Class string `json:"tb_class"`
File string `json:"tb_file"`
}
func HandlerPlus(writer http.ResponseWriter, request *http.Request) {
// 1、解析前端form表单
// 当表单格式为application/x-www-form-urlencoded时, 使用 request.ParseForm()
// 当表单格式为multipart/form-data时, 使用 request.ParseMultipartForm(maxMemory)
err := request.ParseMultipartForm(1024)
if err != nil {
fmt.Println(err)
}
var student Student
// 2、映射到结构体
// 此方法用于设置需要解析到的结构体的属性标签(非常重要,未设置或设置错误,将无法映射)
decoder.SetAliasTag("json")
err = decoder.Decode(&student, request.PostForm)
if err != nil {
fmt.Println(err)
}
// 文件处理部分
// 此处 tb_file 为前端表单文件项属性的name值
file, header, err := request.FormFile("tb_file")
if err != nil {
fmt.Println(err)
}
filePath, err := fileService(file, header)
if err != nil {
fmt.Println(err)
} else {
student.File = filePath
}
// 3、对解析到的对象进行处理, 如存储到数据库等
fmt.Println(student)
var res = map[string]string{
"result": "success",
"name": student.Name,
"phone": student.Phone,
"class": student.Class,
"file": student.File,
}
response, _ := json.Marshal(res)
writer.Header().Set("Content-Type", "application/json")
writer.WriteHeader(http.StatusOK)
writer.Write(response)
}
// 文件处理方法
func fileService(file multipart.File, header *multipart.FileHeader) (string, error) {
timeStr := strconv.Itoa(int(time.Now().Unix())) // 时间戳
filePath := sourcePath + "New" + "_" + timeStr + "_" + header.Filename
_, err := os.Stat(sourcePath)
if err != nil {
os.Mkdir(sourcePath, os.ModePerm)
}
out, err := os.Create(filePath)
if err != nil {
return "", err
}
fmt.Println("the upload file name is : ", header.Filename)
defer out.Close()
_, err = io.Copy(out, file)
if err != nil {
return "", err
}
return filePath, err
}