2.2goweb解析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会隐式调用解析。

例子:

html 复制代码
    <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>
html 复制代码
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)
}
相关推荐
研究司马懿6 小时前
【云原生】Gateway API高级功能
云原生·go·gateway·k8s·gateway api
梦想很大很大20 小时前
使用 Go + Gin + Fx 构建工程化后端服务模板(gin-app 实践)
前端·后端·go
lekami_兰1 天前
MySQL 长事务:藏在业务里的性能 “隐形杀手”
数据库·mysql·go·长事务
却尘1 天前
一篇小白也能看懂的 Go 字符串拼接 & Builder & cap 全家桶
后端·go
ん贤1 天前
一次批量删除引发的死锁,最终我选择不加锁
数据库·安全·go·死锁
mtngt112 天前
AI DDD重构实践
go
Grassto3 天前
12 go.sum 是如何保证依赖安全的?校验机制源码解析
安全·golang·go·哈希算法·go module
Grassto5 天前
11 Go Module 缓存机制详解
开发语言·缓存·golang·go·go module
程序设计实验室6 天前
2025年的最后一天,分享我使用go语言开发的电子书转换工具网站
go
我的golang之路果然有问题6 天前
使用 Hugo + GitHub Pages + PaperMod 主题 + Obsidian 搭建开发博客
golang·go·github·博客·个人开发·个人博客·hugo