快速上手GO的net/http包,个人学习笔记

news2025/7/15 11:16:36

更多个人笔记:(仅供参考,非盈利)
gitee: https://gitee.com/harryhack/it_note
github: https://github.com/ZHLOVEYY/IT_note

针对GO中net/http包的学习笔记

基础快速了解

创建简单的GOHTTP服务

func main() {  
    http.HandleFunc("/hello", sayHello)  
    http.ListenAndServe(":8080", nil) //创建基本服务  
}  
  
func sayHello(w http.ResponseWriter, r *http.Request) {  
    w.Write([]byte("Hello, World!"))  
}

访问8080/hello进行测试

Handler接口定义:(这部分后面又详细解释)

type Handler interface {  
    ServeHTTP(ResponseWriter, *Request)  
}
//只要有ServeHTTP方法就行

可以自己实现这个接口

同时http提供了handlerFunc结构体

type HandlerFunc func(ResponseWriter, *Request)
// ServeHTTP calls f(w, r).
func (f HandlerFunc) ServeHTTP(w ResponseWriter, r *Request) {
f(w, r)
}
//本质上就是调用自身,因为也是一个函数,不过serveHTTP的内容自己可以定义改动

和之前的HandleFunc区分,HandleFunc是用来给不同路径绑定方法的,少一个r

那么只要满足Handlerfunc 的签名形式,就可以进行类型转换

  • 类型转换的正常的理解例子
type yes func(int, int) int  
func (y yes) add(a, b int) int {  
    return a + b  
}  
func multiply(a, b int) int {  
	fmt.Println(a * b)
    return a * b  
}  
func main() {  
    multiply(1, 2)   //2
    ans := yes(multiply) //将multiply进行转换  
    res := ans.add(1, 2)  
    fmt.Println(res)   //3
}

http.HandleFunc("/hello", hello)

这个后面的签名只要是 func(ResponseWriter, *Request)就可以了
但是

http.ListenAndServe(":8080", referer)

这个后面的函数需要是满足Handler接口,有serveHTTP方法

尝试搭建检测是在query中有name = red
即http://localhost:8080/hello?name=red
发现会有重复覆盖路由的问题,因为listenandServe会拦截所有的路由,后面再解决


type CheckQueryName struct {
	wantname string
	handler  http.Handler
}

func (this *CheckQueryName) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	queryParams := r.URL.Query() //获取get请求的query
	name := queryParams.Get("name")
	if name == "red" {
		this.handler.ServeHTTP(w, r) //其实就是调用本身,下面变为checkforname了
	} else {
		w.Write([]byte("not this name"))
	}
}

func checkforname(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("check is ok"))
}

func hello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello"))
}

func main() {
	thecheck := &CheckQueryName{ //用&因为serveHTTP方法定义在指针接收器上
		wantname: "red",
		handler:  http.HandlerFunc(checkforname),
	}
	http.HandleFunc("/hello", hello)       //满足func(ResponseWriter, *Request)签名就可以
	http.ListenAndServe(":8080", thecheck) //直接监视8080的所有端口,拦截所有路由
}
编写简单的GET请求客户端

利用defaultclient或者自己定义client都可以

func main() {
	resp, err := http.DefaultClient.Get("https://api.github.com")
	if err != nil {
		panic(err)
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
	time.Sleep(time.Second * 2)  //等待获取请求
}

但是如果把网址换成baidu.com就会获取不到,这是因为转发,以及没有User-agent的问题

编写自定义的GET请求客户端

利用http.Client可以进行自定义

func main() {
	client := &http.Client{
		// 允许重定向
		CheckRedirect: func(req *http.Request, via []*http.Request) error {
			return nil
		},
	}

	req, err := http.NewRequest("GET", "https://www.baidu.com", nil)
	if err != nil {
		panic(err)
	}
	// 添加请求头
	req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36")

	resp, err := client.Do(req) 
	//do执行HTTP请求的整个周期包括请求准备,建立连接,发送请求,请求重定向,接收响应等等
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(body))
	time.Sleep(time.Second * 2)
}

发现可以接受到baidu的网页html信息

编写默认的post请求客户端
func main() {
	postData := strings.NewReader(`{"name": "张三", "age": 25}`)
	resp, err := http.DefaultClient.Post(
		"http://localhost:8080/users",
		"application/json",  
		postData,
	)
	if err != nil {
		fmt.Printf("POST请求失败: %v\n", err)
		return
	}
	defer resp.Body.Close()

	body, _ := ioutil.ReadAll(resp.Body)
	fmt.Printf("POST响应: %s\n", string(body))
}

string.NewReader是一种方法将格式改为io reader可以读取的形式,接收的话也可以postData.Read读取。
这一类的方式比较多,不一一汇总

对应的server.go (需要在终端中go run server.go )两个终端分别运行服务端,客户端

func main() {
	// 处理 /users 路径的 POST 请求
	http.HandleFunc("/users", func(w http.ResponseWriter, r *http.Request) {
		// 只允许 POST 方法
		if r.Method != http.MethodPost {
			w.WriteHeader(http.StatusMethodNotAllowed)
			fmt.Fprintf(w, "只支持 POST 方法")
			return
		}
		// 读取请求体
		body, err := ioutil.ReadAll(r.Body)
		if err != nil {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintf(w, "读取请求失败: %v", err)
			return
		}
		defer r.Body.Close()
		// 解析 JSON 数据,放入user中
		var user User
		if err := json.Unmarshal(body, &user); err != nil {
			w.WriteHeader(http.StatusBadRequest) //写入状态码
			fmt.Fprintf(w, "JSON 解析失败: %v", err)
			return
		}
		// 设置响应头
		w.Header().Set("Content-Type", "application/json")
		// 构造响应数据
		response := map[string]interface{}{
			"message": "success",
			"data": map[string]interface{}{
				"name": user.Name,
				"age":  user.Age,
			},
		}

		// 返回 JSON 响应
		json.NewEncoder(w).Encode(response) //将 response 对象转换为 JSON 格式并写入响应
		//等价于:
		// jsonData, err := json.Marshal(response)
		// if err != nil {
		// 	w.WriteHeader(http.StatusInternalServerError)
		// 	return
		// }
		// w.Write(jsonData)
	})

	// 启动服务器
	fmt.Println("服务器启动在 :8080 端口...")
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

多路复用器

DefaultServeMux一般不会使用,因为会有冲突等等问题,所以一般用NewServeMux直接创建

type apiHandler struct{}

func (apiHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"message": "API response"}`)
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/api/", apiHandler{})  //多引入结构体,后面会知道有好处
	// mux.HandleFunc("/api/", func(w http.ResponseWriter, req *http.Request) {
    //     w.Header().Set("Content-Type", "application/json")
    //     fmt.Fprintf(w, `{"message": "API response from HandleFunc"}`)
    // })   //和上面等效
	mux.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		if req.URL.Path != "/" {
			http.NotFound(w, req)
			return
		}
		fmt.Fprintf(w, "Welcome to the home page!")
	})

	fmt.Println("Server running on :8080")
	http.ListenAndServe(":8080", mux)
	//用server:= &http.Server创建地址和handler,然后server.ListenAndServe也是一种表现方式
	
}

mux和http.HandleFunc()的区别:
mux 可以创建多个路由器实例,用于不同的目的。同时可以为不同的路由器配置不同的中间件(用着先)

第三方有库httprouter,比如可以解决url不能是变量代表的问题,了解就行
更多都是使用restful API进行开发的~目前的了解有个概念就行

处理器函数

Handle

注册处理器过程中调用的函数:Handle

type username struct {
	name string
}

func (this *username) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	fmt.Fprintf(w, "%s", this.name)
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/jack", &username{name: "jack"}) //会调用对应的serveHTTP方法
	mux.Handle("/lily", &username{name: "lily"})
	//可以为不同的路径使用相同的处理器结构体,但传入不同的参数
	//这就是比用handleFunc()更灵活的地方
	server := &http.Server{
		Addr:    ":8080",
		Handler: mux,
	}
	if err := server.ListenAndServe(); err != nil { //防止错误
		panic(err)
	}
}
HandlleFunc

处理器函数:HandleFunc
(注意不是HandlerFunc,没有r !! HandleFunc 是处理器函数)
之前已经学习过,定义就是
func HandleFunc(pattern string, handler func(ResponseWriter, *Request))

深入源代码会发现内部也是借助serveMux对象,从而实现了Handler的ServeHTTP()方法的

Handler

Handler就是处理器接口,实现ServeHTTP方法的,之前展示过

type Handler interface {
	ServeHTTP(ResponseWriter, *Request)
}
HandlerFunc

HandlerFunc是结构体,用于实现接口的
定义:

type HandlerFunc func(ResponseWriter, *Request)

用于连接处理器Handle和处理器函数HandleFunc,它实现了 Handler 接口,使得函数可以直接当作处理器使用:

  • 理解“连接连接处理器Handle和处理器函数HandleFunc”:(之前也学过)
// 方式一:普通函数
func hello(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello!")
}
// 注册方式一:将函数转换为 HandlerFunc
http.Handle("/hello", http.HandlerFunc(hello))

// 方式二:直接使用 HandleFunc
http.HandleFunc("/hello", hello)

处理请求

请求分为:请求头,请求URL,请求体等

html表单的enctype属性

在postman的body部分可以查看

  • application/x-www-form-urlencode
    url方式编码,较为通用,get和post都可以用
  • multipart/form-data
    通常适配post方法提交
  • text/plain
    适合传递大量数据

ResponseWriter接口涉及方法

  • 补充:
    fmt.Fprint和fmt.Fprintln能够写入ResponseWriter是因为ResponseWriter实现了io.Writer接口,fmt.Fprint/Fprintln将数据按格式转换为字节流(如字符串、数字等),最终调用io.Writer的Write方法
Writeheader

curl -i localhost:8080/noAuth 或者使用postman进行验证

func noAuth(w http.ResponseWriter, r *http.Request) {
	w.WriteHeader(401)
	fmt.Fprint(w, "没有授权,你需要认证后访问")
}

func main() {
	http.HandleFunc("/noAuth", noAuth)
	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		fmt.Println(err)
	}
}

Header

调用了Writeheader后的话就不能对响应头进行修改了
curl -i http://localhost:8081/redirect (可以直接看到301)或者postman验证

  • 重定向代码
func Redirect(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Location", "http://localhost:8080/hello")
	// 必须使用包括http的完整的URL!
	w.WriteHeader(301)
}
func main() {
	http.HandleFunc("/redirect", Redirect)
	if err := http.ListenAndServe(":8081", nil); err != nil {
		panic(err)
	}
}
  • 主服务代码
func sayHello(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("hello !!"))
}

func main() {
	http.HandleFunc("/hello", sayHello)
	http.ListenAndServe(":8080", nil) //创建基本服务
}

write

之前都有demo,就是写入返回,注意需要是[]byte()这样的表示形式
如果不知道content-type格式可以通过数据的前512 比特进行确认

除了一般的文本字符串之外,还可以返回html和json,下面给出json的示范

type language struct {
	Language string `json:"language"` //反引号
	 // 字段名首字母需要大写才能被 JSON 序列化!!!!
}

func uselanguage(w http.ResponseWriter, r *http.Request) {
	uselanguageis := language{Language: "en"}
	message, err := json.Marshal(uselanguageis)
	if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
	w.Header().Set("Content-Type", "application/json") //通过json形式传递
	w.Write(message)
}

func main() {
	http.HandleFunc("/lan", uselanguage)
	if err := http.ListenAndServe(":8080", nil); err != nil {
		panic(err)
	}
}

注意一些格式上的细节,比如字段名首字母需要大写才能被 JSON 序列化

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2342219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Cephalon端脑云:神经形态计算+边缘AI·重定义云端算力

前引:当算力不再是“奢侈品” ,在人工智能、3D渲染、科学计算等领域,算力一直是横亘在个人与企业面前的“高墙”。高性能服务器价格动辄数十万元,专业设备维护成本高,普通人大多是望而却步。然而,Cephalon算…

Redis的过期删除策略和内存淘汰策略

🤔 过期删除和内存淘汰乍一看很像,都是做删除操作的,这么分有什么意思? 首先,设置过期时间我们很熟悉,过期时间到了,我么的键就会被删除掉,这就是我们常认识的过期删除,…

MySQL:数据库设计

目录 一、范式 二、第一范式 二、第二范式 三、第三范式 四、设计 (1)一对一关系 (2)一对多关系 (3)多对多关系 一、范式 数据库的范式是一种规则(规范),如果我们…

synchronized关键字的实现

Java对象结构 synchronized锁升级过程 为了优化synchronized锁的效率,在JDK6中,HotSpot虚拟机开发团队提出了锁升级的概念,包括偏向锁、轻量级锁、重量级锁等,锁升级指的就是“无锁 --> 偏向锁 --> 轻量级锁 --> 重量级…

opencv 图像的旋转

图像的旋转 1 单点旋转2. 图片旋转(cv2.getRotationMatrix2D)3. 插值方法3.1 最近邻插值(cv2.INTER_NEAREST)3.2 双线性插值(cv2.INTER_LINEAR)3.3 像素区域插值(cv2.INTER_AREA)3.4 双三次插值(cv2.INTER_CUBIC&#…

【多线程】线程互斥 互斥量操作 守卫锁 重入与线程安全

文章目录 Ⅰ. 线程互斥概念Ⅱ. 互斥锁的概念Ⅲ. 互斥锁的接口一、互斥锁的定义二、初始化互斥锁三、销毁互斥锁四、互斥量的加锁和解锁① 加锁接口② 解锁接口五、改进买票系统💥注意事项Ⅳ. 互斥锁的实现原理一、问题引入二、复习知识三、实现原理Ⅴ. 封装锁对象 &&…

空闲列表:回收和再利用

空闲列表:回收和再利用 手动与自动内存管理 手动管理:程序员需要明确地分配和释放内存。自动管理:例如使用垃圾收集器(GC),它能够自动检测并回收未使用的对象,不需要程序员干预。 对于某些数据结构如B树,…

计算机组成与体系结构:直接内存映射(Direct Memory Mapping)

目录 CPU地址怎么找到真实的数据? 内存映射的基本单位和结构 1. Pages(页)——虚拟地址空间的基本单位 2. Frames(页框)——物理内存空间的基本单位 3. Blocks(块)——主存和缓存之间的数据…

STM32提高篇: 蓝牙通讯

STM32提高篇: 蓝牙通讯 一.蓝牙通讯介绍1.蓝牙技术类型 二.蓝牙协议栈1.蓝牙芯片架构2.BLE低功耗蓝牙协议栈框架 三.ESP32-C3中的蓝牙功能1.广播2.扫描3.通讯 四.发送和接收 一.蓝牙通讯介绍 蓝牙,是一种利用低功率无线电,支持设备短距离通信的无线电技…

SpringMVC处理请求映射路径和接收参数

目录 springmvc处理请求映射路径 案例:访问 OrderController类的pirntUser方法报错:java.lang.IllegalStateException:映射不明确 核心错误信息 springmvc接收参数 一 ,常见的字符串和数字类型的参数接收方式 1.1 请求路径的…

【程序员 NLP 入门】词嵌入 - 上下文中的窗口大小是什么意思? (★小白必会版★)

🌟 嗨,你好,我是 青松 ! 🌈 希望用我的经验,让“程序猿”的AI学习之路走的更容易些,若我的经验能为你前行的道路增添一丝轻松,我将倍感荣幸!共勉~ 【程序员 NLP 入门】词…

从物理到预测:数据驱动的深度学习的结构化探索及AI推理

在当今科学探索的时代,理解的前沿不再仅仅存在于我们书写的方程式中,也存在于我们收集的数据和构建的模型中。在物理学和机器学习的交汇处,一个快速发展的领域正在兴起,它不仅观察宇宙,更是在学习宇宙。 AI推理 我们…

大模型AI的“双刃剑“:数据安全与可靠性挑战与破局之道

在数字经济蓬勃发展的浪潮中,数据要素已然成为驱动经济社会创新发展的核心引擎。从智能制造到智慧城市,从电子商务到金融科技,数据要素的深度融合与广泛应用,正以前所未有的力量重塑着产业格局与经济形态。 然而,随着…

操作系统概述与安装

主流操作系统概述 信创平台概述 虚拟机软件介绍与安装 windows server 安装 centos7 安装 银河麒麟V10 安装 一:主流服务器操作系统 (1)Windows Server 发展历程: 1993年推出第一代 WindowsNT(企业级内核&am…

开发了一个b站视频音频提取器

B站资源提取器-说明书 一、功能说明 本程序可自动解密并提取B站客户端缓存的视频资源,支持以下功能: - 自动识别视频缓存目录 - 将加密的.m4s音频文件转换为标准MP3格式 - 将加密的.m4s视频文件转换为标准MP4格式(合并音视频流)…

基于javaweb的SpringBoot校园服务平台系统设计与实现(源码+文档+部署讲解)

技术范围:SpringBoot、Vue、SSM、HLMT、Jsp、PHP、Nodejs、Python、爬虫、数据可视化、小程序、安卓app、大数据、物联网、机器学习等设计与开发。 主要内容:免费功能设计、开题报告、任务书、中期检查PPT、系统功能实现、代码编写、论文编写和辅导、论文…

PHYBench:首个大规模物理场景下的复杂推理能力评估基准

2025-04-23, 由北京大学物理学院和人工智能研究所等机构共同创建的 PHYBench 数据集,这是一个专门用于评估大型语言模型在物理场景下的复杂推理能力的高质量基准。该数据集包含 500 道精心策划的物理问题,覆盖力学、电磁学、热力学、光学、现代物理和高级…

Red:1靶场环境部署及其渗透测试笔记(Vulnhub )

环境介绍: 靶机下载: https://download.vulnhub.com/red/Red.ova 本次实验的环境需要用到VirtualBox(桥接网卡),VMware(桥接网卡)两台虚拟机(网段都在192.168.152.0/24&#xff0…

深入详解人工智能数学基础——概率论中的KL散度在变分自编码器中的应用

🧑 博主简介:CSDN博客专家、CSDN平台优质创作者,高级开发工程师,数学专业,10年以上C/C++, C#, Java等多种编程语言开发经验,拥有高级工程师证书;擅长C/C++、C#等开发语言,熟悉Java常用开发技术,能熟练应用常用数据库SQL server,Oracle,mysql,postgresql等进行开发应用…

测试模版x

本篇技术博文摘要 🌟 引言 📘 在这个变幻莫测、快速发展的技术时代,与时俱进是每个IT工程师的必修课。我是盛透侧视攻城狮,一名什么都会一丢丢的网络安全工程师,也是众多技术社区的活跃成员以及多家大厂官方认可人员&a…