Gin源码分析 – Context之PostForm

1 介绍

对于表单数据的获取Gin也提供了一组方法,本文将对这组方法的使用以及源码进行详细的分析,下面的例子模拟一个最简单的登陆。

package main

import (
	"net/http"

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

func main() {
	r := gin.Default()
	r.POST("/login", func(c *gin.Context) {
		username := c.PostForm("username")
		password := c.PostForm("password")
		c.JSON(http.StatusOK, gin.H{
			"username": username,
			"password": password,
		})
	})
	r.Run(":9999")
}

通过curl发送POST请求,显示结果如下:

curl -d "username=TerryPro&password=123456" 127.0.0.1:9999/login 
{"password":"123456","username":"TerryPro"}

2 函数概述

Gin提供的PostForm函数与Query基本上一一对应的,具体情况见下表

Query方法

PostForm方法

说明

Query

PostForm

获取key对应的值,不存在返回空字符串

GetQuery

GetPostForm

获取key对应的值,并且返回bool标识,标识成功或者失败

QueryArray

PostFormArray

获取key对应的值,只是一个字符串数组,不存在返回空字符串数组

GetQueryArray

GetPostFormArray

获取key对应的值,并且返回bool标识,标识成功或者失败

QueryMap

PostFormMap

获取key对应的值,值是一个字符串map[string]string,不存在返回空

GetQueryMap

GetPostFomMap

获取key对应的值,值是一个字符串map[string]string,并且返回bool标识,标识成功或者失败

DefaultQuery

DefaultPostForm

key不存在时返回一个默认值

3 PostForm分析

func (c *Context) PostForm(key string) string {
	value, _ := c.GetPostForm(key)
	return value
}

func (c *Context) DefaultPostForm(key, defaultValue string) string {
	if value, ok := c.GetPostForm(key); ok {
		return value
	}
	return defaultValue
}

func (c *Context) GetPostForm(key string) (string, bool) {
	if values, ok := c.GetPostFormArray(key); ok {
		return values[0], ok
	}
	return "", false
}

func (c *Context) GetPostFormArray(key string) ([]string, bool) {
	c.initFormCache()
	if values := c.formCache[key]; len(values) > 0 {
		return values, true
	}
	return []string{}, false
}

从上面的代码可以看出PostForm的调用方式和Query也是一致的:

(1)PostForm和DefaultPostForm内部调用GetPostForm;

(2)GetPostForm调用GetPostFormArray,Context中使用了另外一个变量formCache缓存PostForm参数,调用
Request.ParseMultipartForm完成参数的解析。

func (c *Context) initFormCache() {
	if c.formCache == nil {
		c.formCache = make(url.Values)
		req := c.Request
		if err := req.ParseMultipartForm(c.engine.MaxMultipartMemory); err != nil {
			if err != http.ErrNotMultipart {
				debugPrint("error on parse multipart form array: %v", err)
			}
		}
		c.formCache = req.PostForm
	}
}

type Context struct {
	// formCache use url.ParseQuery cached PostForm contains the parsed form data from POST, PATCH,
	// or PUT body parameters.
	formCache url.Values
}

4 PostFormMap分析

// PostFormMap returns a map for a given form key.
func (c *Context) PostFormMap(key string) map[string]string {
	dicts, _ := c.GetPostFormMap(key)
	return dicts
}

// GetPostFormMap returns a map for a given form key, plus a boolean value
// whether at least one value exists for the given key.
func (c *Context) GetPostFormMap(key string) (map[string]string, bool) {
	c.initFormCache()
	return c.get(c.formCache, key)
}

(1)PostFormMap调用GetPostFormMap返回一个map[string]string;

(2)GetPostFormMap首先完成参数解析并进行缓存,然后调用get方式完成map的获取,这个函数在Query一文件已经分析过。

下面的例子给出了使用方法以及使用curl进行验证的结果。

	r.POST("/loginMap", func(c *gin.Context) {
		params := c.PostFormMap("params")
		c.JSON(http.StatusOK, gin.H{
			"params": params,
		})
	})
curl -d "params[username]=TerryPro¶ms[password]=123456" 127.0.0.1:9999/loginMap 
{"params":{"password":"123456","username":"TerryPro"}}

5 小节

如果看过Query的分析则本文的内容还是相对简单的。