在Go语言开发Web服务时,处理客户端发来的HTTP请求是核心工作之一,其中解析查询参数和请求体数据是最基础也最容易出问题的环节,需要根据请求的不同类型采用对应的解析方式。

查询参数的解析方法
查询参数是拼接在URL后面的键值对,格式为?key1=value1&key2=value2,Go的net/http包提供了专门的方法来处理这类参数。
获取单个查询参数
可以通过r.URL.Query().Get(key)方法获取指定key的查询参数,当参数不存在时返回空字符串。
package main
import (
"fmt"
"net/http"
)
func queryHandler(w http.ResponseWriter, r *http.Request) {
// 只允许GET请求
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "只支持GET请求")
return
}
// 获取name参数
name := r.URL.Query().Get("name")
// 获取age参数
age := r.URL.Query().Get("age")
fmt.Fprintf(w, "姓名:%s,年龄:%s", name, age)
}
func main() {
http.HandleFunc("/query", queryHandler)
http.ListenAndServe(":8080", nil)
}
获取多个同名查询参数
如果同一个key对应多个值,比如?hobby=篮球&hobby=足球,需要使用r.URL.Query()[key]获取字符串切片。
package main
import (
"fmt"
"net/http"
"strings"
)
func multiQueryHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodGet {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "只支持GET请求")
return
}
// 获取所有hobby参数
hobbies := r.URL.Query()["hobby"]
hobbyStr := strings.Join(hobbies, "、")
fmt.Fprintf(w, "爱好:%s", hobbyStr)
}
func main() {
http.HandleFunc("/multi_query", multiQueryHandler)
http.ListenAndServe(":8080", nil)
}
请求体数据的解析方法
请求体数据通常出现在POST、PUT等请求中,常见的格式有表单格式、JSON格式、XML格式等,需要根据不同的Content-Type采用对应的解析方式。
解析表单格式请求体
表单格式的Content-Type通常为application/x-www-form-urlencoded或者multipart/form-data,前者是普通表单,后者常用于文件上传的表单。
首先需要在解析前调用r.ParseForm()或者r.ParseMultipartForm(maxMemory)方法,之后通过r.Form或者r.PostForm获取参数。
package main
import (
"fmt"
"net/http"
)
func formHandler(w http.ResponseWriter, r *http.Request) {
// 只允许POST请求
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "只支持POST请求")
return
}
// 解析表单数据
err := r.ParseForm()
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "解析表单失败:%v", err)
return
}
// r.Form包含URL查询参数和POST表单参数
username := r.Form.Get("username")
password := r.Form.Get("password")
// r.PostForm只包含POST表单参数,不包含URL查询参数
postPassword := r.PostForm.Get("password")
fmt.Fprintf(w, "用户名:%s,表单密码:%s,POST参数密码:%s", username, password, postPassword)
}
func main() {
http.HandleFunc("/form", formHandler)
http.ListenAndServe(":8080", nil)
}
解析JSON格式请求体
当请求的Content-Type为application/json时,需要先将请求体解码为对应的结构体,这里需要使用encoding/json包。
package main
import (
"encoding/json"
"fmt"
"net/http"
)
// 定义接收JSON的结构体
type User struct {
Username string `json:"username"`
Age int `json:"age"`
Email string `json:"email"`
}
func jsonHandler(w http.ResponseWriter, r *http.Request) {
if r.Method != http.MethodPost {
w.WriteHeader(http.StatusMethodNotAllowed)
fmt.Fprintf(w, "只支持POST请求")
return
}
// 检查Content-Type是否为application/json
contentType := r.Header.Get("Content-Type")
if contentType != "application/json" {
w.WriteHeader(http.StatusUnsupportedMediaType)
fmt.Fprintf(w, "不支持的Content-Type:%s", contentType)
return
}
var user User
// 解码请求体到user结构体
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
w.WriteHeader(http.StatusBadRequest)
fmt.Fprintf(w, "解析JSON失败:%v", err)
return
}
defer r.Body.Close()
fmt.Fprintf(w, "用户信息:用户名%s,年龄%d,邮箱%s", user.Username, user.Age, user.Email)
}
func main() {
http.HandleFunc("/json", jsonHandler)
http.ListenAndServe(":8080", nil)
}
注意事项
- 解析查询参数前不需要额外调用解析方法,直接通过
r.URL.Query()即可获取。 - 解析表单请求体前必须先调用
r.ParseForm()或者r.ParseMultipartForm(),否则无法获取到表单参数。 - 解析JSON请求体时,结构体的字段需要导出(首字母大写),并且通过结构体标签指定对应的JSON key,否则无法正确映射数据。
- 请求体读取完成后建议调用
r.Body.Close()释放资源,避免内存泄漏。 - 获取参数后建议做合法性校验,避免空值或者非法数据导致后续业务逻辑出错。
实际开发中可以根据业务需求封装统一的参数解析函数,减少重复代码,同时统一处理参数解析失败的场景。