Go语言的net/http包提供了一些列用于表示HTTP报文的解构。我们可以使用它处理请求和发送响应。其中request结构体代表了客户端发生的请求报文。
核心字段获取方法
1. 请求行信息
通过 http.Request
结构体获取:
go
func handler(w http.ResponseWriter, r *http.Request) {
// 请求方法(GET/POST等)
fmt.Println("Method:", r.Method)
// 完整URL
fmt.Println("URL:", r.URL.String())
urlPath := r.URL.Path // 请求路径(如 "/api/data")
fmt.Println("请求路径:",urlPath)
//直接获取原始查询字符串
rawQuery := r.URL.RawQuery
fmt.Fprintf(w, "Raw Query: %s", rawQuery)
// 方法2:直接获取已解析的 Values(推荐)
values := r.URL.Query()
//url.Values 类型:本质是 map[string][]string,支持多值参数(如 ?tags=go&tags=web)
tags := values["tags"] // 返回字符串切片 []string{"go", "web"}
fmt.Println("查询字符串tags:", tags )
q := values.Get("q") // 返回第一个值,若不存在则返回空字符串
fmt.Println("查询字符串q:", q)
if page, ok := values["page"]; ok {
// 参数存在
fmt.Println("page参数存在:", page)
}else{
fmt.Println("page参数不存在,需要给出默认值:", page)
}
// 协议版本
fmt.Println("Protocol:", r.Proto)
}
RawQuery
中的参数是 已编码 的(如空格为 %20
),需使用 url.QueryUnescape
解码:
decoded, _ := url.QueryUnescape(r.URL.RawQuery)
频繁操作查询参数时,优先使用 r.URL.Query()
缓存解析结果,避免重复解析。
2. 请求头信息
使用 Header
字段读取:
规范化的头字段名
Go 会自动将头字段名规范化(首字母大写),例如:
user-agent
→User-Agent
content-type
→Content-Type
go
//1. 获取单个头信息值
//Get() 方法:返回指定键的第一个值(适用于单值头字段)。
encoding := r.Header.Get("Accept-Encoding") // 返回 "gzip"
//直接访问 map:获取值的切片(需处理空值情况)。
if values := r.Header["User-Agent"]; len(values) > 0 {{
userAgent = values[0]
}}
// 获取多值头信息
//Values() 方法(Go 1.14+):返回所有值的切片。
encodings := r.Header.Values("Accept-Encoding") // ["gzip", "deflate"]
// 遍历所有头部
for key, values := range r.Header {
fmt.Printf("%s: %v\n", key, values)
}
3. 查询参数(URL 参数)
手动解析 URL 中的 Query
参数:
go
// 方法:手动解析(需处理错误)
decoded, _ := url.QueryUnescape(r.URL.RawQuery)
values, err := url.ParseQuery(decoded)
if err != nil {
http.Error(w, "Invalid query", http.StatusBadRequest)
return
}
4. 请求体内容
根据 Content-Type
处理不同格式的请求体:
go
// 读取全部内容(适用于小数据)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, "Read body failed", http.StatusBadRequest)
return
}
fmt.Println("Body:", string(body))
// 关闭Body(必须)
defer r.Body.Close()
a. 表单数据(Form Data)
go
err := r.ParseForm() // 显式解析表单
username := r.FormValue("username") // 获取单个字段(也可以获取url传递过来的参数)
allFormData := r.PostForm // 获取全部表单数据(map类型,不含url传递过来的参数)
allFormData2 := r.Form // 获取全部表单数据(map类型,包含url传递过来的参数)
FormValue会隐式调用解析。
例子:
javascript
<div class="login-container">
<h2>系统登录</h2>
<form action="/api?username=lisi&pwd=wangwu" method="post">
<div class="form-group">
<label>用户名:</label>
<input type="text" name="uname" value="赵六" required>
</div>
<div class="form-group">
<label>密码:</label>
<input type="password" name="pwd" value="123" required>
</div>
<button class="btn-login" type="submit">登录</button>
</form>
</div>
scss
func apiHandler(w http.ResponseWriter, r *http.Request) {
username1 := r.FormValue("username")
uname1 := r.FormValue("uname")
pwd1 := r.FormValue("pwd")
allFormData := r.PostForm
username2 := allFormData["username"]
uname2 := allFormData["uname"]
pwd2 := allFormData["pwd"]
allData := r.Form
username3 := r.Form["username"]
uname3 := r.Form["uname"]
pwd3 := r.Form["pwd"]
fmt.Println("FormValue方法:", username1, uname1)
fmt.Println("FormValue方法:", pwd1)
fmt.Println("==========")
fmt.Println("PostForm:", allFormData)
fmt.Println("PostForm:", username2, uname2)
fmt.Println("PostForm:", pwd2)
fmt.Println("==========")
fmt.Println("allData:", allData)
fmt.Println("allData:", username3, uname3)
fmt.Println("allData:", pwd3)
}
b. JSON 数据
go
body, _ := io.ReadAll(r.Body) // 读取原始Body
var data map[string]interface{}
json.Unmarshal(body, &data) // 反序列化到结构体
c. 文件上传(Multipart)
go
package main
import (
"fmt"
"io/ioutil"
"net/http"
)
func uploadHandler(w http.ResponseWriter, r *http.Request) {
// 限制请求体大小(防大文件攻击)
r.ParseMultipartForm(10 << 20) // 10MB
// 获取普通表单字段
title := r.FormValue("title")
// 处理文件上传
file, _, err := r.FormFile("file")
if err != nil {
http.Error(w, "文件上传失败", http.StatusBadRequest)
return
}
defer file.Close()
// 保存文件
data, _ := ioutil.ReadAll(file)
ioutil.WriteFile("/uploads/"+title, data, 0644)
fmt.Fprintln(w, "上传成功")
}
func main() {
http.HandleFunc("/upload", uploadHandler)
http.ListenAndServe(":8080", nil)
}
Go语言处理Cookie
设置Cookie
go
func setCookieHandler(w http.ResponseWriter, r *http.Request) {
// 基础设置(过期时间7天)
cookie := &http.Cookie{
Name: "auth_token",
Value: "abc123xyz",
Path: "/",
Expires: time.Now().Add(7 * 24 * time.Hour),
HttpOnly: true,
Secure: true, // 仅HTTPS传输
SameSite: http.SameSiteLaxMode,
}
http.SetCookie(w, cookie)
// 快捷设置方式
http.SetCookie(w, &http.Cookie{
Name: "preferences",
Value: "dark_mode",
MaxAge: 3600, // 秒数
})
}
获取Cookie
go
func getCookieHandler(w http.ResponseWriter, r *http.Request) {
// 获取单个Cookie
cookie, err := r.Cookie("auth_token")
if err != nil {
if err == http.ErrNoCookie {
fmt.Fprintln(w, "Cookie不存在")
return
}
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
fmt.Fprintf(w, "Cookie值: %s", cookie.Value)
// 获取所有Cookie
for _, c := range r.Cookies() {
fmt.Fprintf(w, "%s: %s\n", c.Name, c.Value)
}
}
Go 标准库(net/http
)不直接支持 Session。
- HTTP 协议本身是无状态的,Session 需要开发者自行实现或依赖第三方库
- 推荐方案:
生产环境:使用 gorilla/sessions 或框架中间件(如 Gin 的 gin-contrib/sessions)。
简单场景:手动实现内存存储(适合学习或小型项目)。
原生实现代码
go
// Session存储中心(线程安全)
var sessionStore = struct {
sync.RWMutex
store map[string]*Session
}{store: make(map[string]*Session)}
// Session数据结构
type Session struct {
ID string
Data map[string]interface{}
CreatedAt time.Time
ExpiresAt time.Time
}
// 全局配置
const (
SessionCookieName = "gosession"
SessionDuration = 24 * time.Hour
)
func CreateSession(w http.ResponseWriter) *Session {
sessionID := generateSessionID()
session := &Session{
ID: sessionID,
Data: make(map[string]interface{}),
CreatedAt: time.Now(),
ExpiresAt: time.Now().Add(SessionDuration),
}
// 线程安全存储
sessionStore.Lock()
sessionStore.store[sessionID] = session
sessionStore.Unlock()
// 设置客户端Cookie
http.SetCookie(w, &http.Cookie{
Name: SessionCookieName,
Value: sessionID,
Path: "/",
HttpOnly: true,
Secure: true,
SameSite: http.SameSiteLaxMode,
Expires: session.ExpiresAt,
})
return session
}
func generateSessionID() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic(err) // 生产环境应更优雅处理
}
return base64.URLEncoding.EncodeToString(b)
}
func GetSession(r *http.Request) (*Session, error) {
cookie, err := r.Cookie(SessionCookieName)
if err != nil {
return nil, err
}
sessionStore.RLock()
defer sessionStore.RUnlock()
if session, exists := sessionStore.store[cookie.Value]; exists {
if time.Now().Before(session.ExpiresAt) {
return session, nil
}
// 自动清理过期session
delete(sessionStore.store, cookie.Value)
}
return nil, fmt.Errorf("session无效或已过期")
}
func DestroySession(w http.ResponseWriter, r *http.Request) {
if cookie, err := r.Cookie(SessionCookieName); err == nil {
// 服务端删除
sessionStore.Lock()
delete(sessionStore.store, cookie.Value)
sessionStore.Unlock()
// 客户端清除
http.SetCookie(w, &http.Cookie{
Name: SessionCookieName,
Value: "",
Path: "/",
Expires: time.Unix(0, 0),
MaxAge: -1,
})
}
}
// 启动定期清理goroutine
func StartSessionGC(interval time.Duration) {
go func() {
for {
time.Sleep(interval)
cleanExpiredSessions()
}
}()
}
func cleanExpiredSessions() {
sessionStore.Lock()
defer sessionStore.Unlock()
now := time.Now()
for id, session := range sessionStore.store {
if now.After(session.ExpiresAt) {
delete(sessionStore.store, id)
}
}
}