goweb解析http请求信息

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-agentUser-Agent
  • content-typeContent-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)
        }
    }
}

相关推荐
学习OK呀3 小时前
从 java8 升级 java17 的调整
java·后端
莫克3 小时前
resources\application.properties 配置大全
后端
王中阳Go3 小时前
go中的singleflight是如何实现的?
后端
AAA修煤气灶刘哥3 小时前
缓存世界的三座大山:穿透、击穿、雪崩,今天就把它们铲平!
redis·分布式·后端
用户4099322502124 小时前
需求驱动测试:你的代码真的在按需行事吗?
后端·ai编程·trae
双向334 小时前
前后端接口调试提效:Postman + Mock Server 的工作流
后端
许苑向上4 小时前
Spring Boot 的注解是如何生效的
java·spring boot·后端
Apifox4 小时前
如何让 Apifox 发布的在线文档具备更好的调试体验?
前端·后端·测试