Golang net/http标准库常用方法(三)

news2024/10/7 0:05:17

大家好,针对Go语言 net/http 标准库,将梳理的相关知识点分享给大家~~
围绕 net/http 标准库相关知识点还有许多章节,请大家多多关注。
文章中代码案例只有关键片段,完整代码请查看github仓库:https://github.com/hltfaith/go-example/tree/main/net-http

本章节案例,请大家以 go1.16+ 版本以上进行参考。

net/http标准库系列文章

  • Golang net/http标准库常用请求方法(一)
  • Golang net/http标准库常用方法(二)
  • Golang net/http标准库常用方法(三)

本节内容

  • ProxyFromEnvironment() 函数
  • ProxyURL() 函数
  • Serve() 函数
  • ServeContent() 函数
  • DetectContentType() 函数
  • MaxBytesReader() 函数

ProxyFromEnvironment()

ProxyFromEnvironment()函数,用于读取所在环境的环境变量返回代理地址。比如环境变量HTTP_PROXYHTTPS_PROXYNO_PROXY,如果在 NO_PROXY 排除的地址则不进行代理。
代理地址格式可以是完整的URL,也可以是host[:port]。支持 HTTPHTTPSSOCKS5代理。
如果环境中未定义代理,或者NO_PROXY定义的给定请求不应使用代理,则返回nil URLnil错误。如果 req.URL.Host 地址为 localhost 加或没加端口,都会返回 nil 错误。
函数原型

func ProxyFromEnvironment(req *Request) (*url.URL, error)

函数使用
proxyfromenvironment.go

func main() {
	os.Setenv("HTTP_PROXY", "http://127.0.0.1:12345")
	req, err := http.NewRequest("GET", "http://example.com", nil)

	if err != nil {
		panic(err)
	}
	url, err := http.ProxyFromEnvironment(req)
	if err != nil {
		panic(err)
	}
	fmt.Println(url)
}

案例中 http.ProxyFromEnvironment(req) 仅会把读取环境变量 HTTP_PROXY 的代理地址,在我们使用 http.NewRequest() 请求时,不会使用代理请求。
下面通过 ProxyURL() 函数案例,发起代理请求。

ProxyURL()

ProxyURL() 作用是返回一个代理函数主要用于在 Transport{} 类型中,其参数是代理地址。
函数原型

func ProxyURL(fixedURL *url.URL) func(*Request) (*url.URL, error)

举例:使用代理发送 HTTP 请求。
proxyurl.go

func main() {
	url, err := url.Parse("http://188.68.176.2:8080")
	if err != nil {
		panic(err)
	}
	client := http.Client{
		Transport: &http.Transport{
			Proxy:           http.ProxyURL(url),
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
		},
	}
	res, err := client.Get("http://baidu.com")
	if err != nil {
		panic(err)
	}
	b, _ := httputil.DumpRequest(res.Request, false)
	fmt.Println(string(b))
}

上述例子中, 将代理函数ProxyURL(url)通过Transport{}类型封装好后,向目标服务发送GET请求。
Client{}Transport{}类型后续文章将详细讲解。

注:代理地址,可以参考 https://www.kuaidaili.com/free/fps/ 用于测试使用。

上面案例,也可以将 http.ProxyURL() 函数改成 ProxyFromEnvironment() 用环境变量的方式。
proxyurl2.go

func main() {
	url, err := url.Parse("http://google.com")
	if err != nil {
		panic(err)
	}

	os.Setenv("HTTP_PROXY", "http://127.0.0.1:7890")
	client := http.Client{
		Transport: &http.Transport{
			Proxy:           http.ProxyFromEnvironment,
			TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, // 跳过https
		},
	}

	req := http.Request{
		Method: "GET",
		URL:    url,
		Header: map[string][]string{
			"Proxy-Connection": {"keep-alive"},
		},
	}
	res, err := client.Do(&req)
	if err != nil {
		panic(err)
	}
	defer res.Body.Close()
}

这里是通过我本地环境的代理VPN所监听的端口 http://127.0.0.1:7890

下面我通过抓包,大家可以看到执行代理请求的时候源端口 50130是我们请求端,访问的谷歌网站目的端已经变成了 http://127.0.0.1:7890 地址也是我们的代理端,后面的响应也是由代理端给我们请求回应数据包。

Serve()

Serve() 函数,接收监听 HTTP 连接请求,为每个连接创建一个新goroutinegoroutine读取请求,然后调用处理程序来回复它们。
官方建议 handlernil类型, 则默认使用 DefaultServerMux 全局锁机制。 (可以参考上篇文章中有所介绍)
只有当 Listener 返回tls的时候,才支持HTTP/2协议。
Serve() 函数返回非 nil 的报错。
函数原型

func Serve(l net.Listener, handler Handler) error

Serve()函数实际上是调用的 Server{} 类型中封装的一个方法。

func Serve(l net.Listener, handler Handler) error {
	srv := &Server{Handler: handler}
	return srv.Serve(l)
}

例如,上篇文章中介绍的 ListenAndServe()ListenAndServeTLS() 方法它们最终执行都是 Server{}类型中的 Serve() 方法。

函数使用
serve.go

func main() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		panic(err)
	}
	http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
		io.WriteString(w, "帽儿山的枪手!\n")
	})
	log.Panicln(http.Serve(ln, nil))
}

ServeContent()

ServeContent() 函数,使用ReadSeeker所读取的内容回复给用户请求。

ServeContentio.Copy更好的是,他能够合适的处理一批请求,设置MIME类型,并且能够处理文件是否修改的请求。

如果响应的内容类型头没有设置,该函数首先会尝试从文件的文件扩展名推断文件类型。 如果推断不出来,则会读取文件的第一个块并传送给DetectContentType来检测类型。

文件名称也可以不使用。 如果文字名称为空,则服务器不会传送给响应。 如果修改时间不为0,ServeContent会把它放在服务器响应的Last-Modified头里面。 如果客户端请求中包含了If-Modified-Since头,ServeContent会使用modtime来判断是否把内容传给客户端。
contentSeek方法必须能够工作。 ServeContent通过定位到文件结尾来确定文件大小。 *os.File中实现了io.ReadSeeker接口。

函数原型

func ServeContent(w ResponseWriter, req *Request, name string, modtime time.Time, content io.ReadSeeker)
  • 参数 w 服务器响应
  • 参数 req 客户端请求
  • 参数 name 文件名称
  • 参数 modtime 文件的修改时间
  • 参数 content 文件的内容,必须实现 io.ReadSeeker 这个接口中的方法

下面案例使用 ServeContent() 函数实现文件下载功能。
servecontent.go

func main() {
	http.HandleFunc("/download", func(w http.ResponseWriter, r *http.Request) {
		file := "servecontent.go"
		fileBytes, err := ioutil.ReadFile(file)
		if err != nil {
			panic(err)
		}

		mime := http.DetectContentType(fileBytes)
		fileSize := len(string(fileBytes))
		w.Header().Set("Content-Type", mime)
		w.Header().Set("Content-Disposition", "attachment; filename="+file)
		w.Header().Set("Content-Length", strconv.Itoa(fileSize))

		http.ServeContent(w, r, file, time.Now(), bytes.NewReader(fileBytes))
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

首先通过 DetectContentType()函数获取了文件的 MIME 类型,然后将文件转换为 Byte 类型传入 ServeContent() 函数中实现下载功能。
结合上篇文章中介绍的 ServeFile()函数它实现起来更简洁仅需要一行代码实现文件下载,但前提需要知道文件上下文路径。
ServeContent() 函数更适用于当你只能拿到 byte[] 数据时,可以优先使用它。

DetectContentType()

DetectContentType() 该函数实现了一个算法,用来检测指定的数据是否符合该标准http://mimesniff.spec.whatwg.org

最多需要数据的前512个字节,DetectContentType()会返回一个有效的MIME类型。 如果它不能够识别数据,将会返回"application/octet-stream"
函数原型

func DetectContentType(data []byte) string

函数使用

func main() {
	// image/png
	fmt.Println(http.DetectContentType([]byte("\x89PNG\x0D\x0A\x1A\x0A")))
	// image/jpeg
	fmt.Println(http.DetectContentType([]byte("\xFF\xD8\xFF")))
}

注:一些类型的识别,可以参考go源码测试用例。

MaxBytesReader()

MaxBytesReader() 函数,用来保护服务器端,以避免客户端偶然或者恶意发送的长数据请求导致的服务端资源的浪费。

MaxBytesReader()io.LimitReader函数很像。但是它被设计来设置接收的请求体的最大大小。 跟io.LimitReader不同MaxBytesReader()的返回值是一个ReadCloser,当读取超过限制时会返回non-nil错误。 并且当它调用关闭方法的时候会把潜在的读取者(函数/进程)也关闭掉。

函数原型

func MaxBytesReader(w ResponseWriter, r io.ReadCloser, n int64) io.ReadCloser
  • 参数 w服务器响应
  • 参数 r可以指向 req.Body
  • 参数 n限制大小

案例,限制客户端上传数据为10个字节。
maxbytesreader.go

func main() {
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		r.Body = http.MaxBytesReader(w, r.Body, 10)
		_, err := io.Copy(ioutil.Discard, r.Body)
		if err != nil {
			panic(err)
		}
		io.WriteString(w, "200\n")
	})

	log.Fatal(http.ListenAndServe(":8080", nil))
}

下面我们通过 curl 命令模拟客户端请求, 其中body内容已经超出了10个字节

root@hc:~# curl --location --request POST 'http://127.0.0.1:8080' \
--header 'Content-Type: application/json' \
--data-raw '{
    "t": "1234567890"
}'

请求完成后,看到服务端已经提示 请求Body过大

技术文章持续更新,请大家多多关注呀~~

搜索微信公众号,关注我【 帽儿山的枪手 】

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

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

相关文章

工具分享:VsCode注释神器,koro1FileHeader

他是有官方Wiki的。 https://github.com/OBKoro1/koro1FileHeader/wiki/ 项目在GitHub上开源。以下摘录部分wiki,用作介绍分享在这里插入代码片 如何找到setting.json设置模板 简单的输入命令 打开VSCode命令面板: mac: command p window: ctrl p输入> Ope…

windows查看mysql的版本(三种方法)

方法一:在win r 打开 cmd,在cmd命令状态下:mysql --help 回车即可 方法二:在mysql客户端窗口状态下:输入status并回车即可 在计算机开始菜单搜索以上应用打开即可进入mysql客户端窗口。 方法三:在mys…

[技术报告]InternLM2 Technical Report

摘要 像ChatGPT和GPT-4这样的大型语言模型(llm)的进化引发了人们对人工通用智能(AGI)出现的讨论。然而,在开源模型中复制这种进步一直是一个挑战。本文介绍了InternLM2,这是一个开源的大语言模型&#xff…

项目9-网页聊天室2(登录)

0.前端知识储备 Ajax请求中的async:false/true的作用 - front-gl - 博客园 (cnblogs.com) 01.前端页面展示 02.后端代码 2.1 CONTROLLER RequestMapping("/login")public Result login(String username, String password, HttpSession httpSession){User user …

零基础PHP入门(一)选择IDE和配置环境

配置环境 官网下载安装包,windows https://windows.php.net/download#php-8.3 我是下载的最新版,也可以切换其他版本 https://windows.php.net/downloads/releases/archives/ 下载好压缩文件后,双击解压到一个目录 D:\soft\php 复制ph…

指针变量加1为什么地址加了4?

#include <stdio.h> #include <stdlib.h> int main() {int a 1;int* ptr;ptr &a;printf("ptr is: %p\n", ptr);ptr;printf("ptr1 is: %p\n", ptr);return 0; }在上述代码中&#xff0c;首先定义了一个int类型的变量a&#xff0c;和一个i…

西门子CPU与汇川伺服通信与控制

西门子CPU与汇川620F伺服通信与控制 一、西门子CPU与汇川620F伺服通信与控制1、器件准备2、伺服软件设置3、PLC添加汇川伺服描述文件4、PLC编程调试5、总结 二、西门子s7-1500限位信号接到伺服的方法1、通过默认报文获取限位信号2、添加自定义报文获取限位信号3、总结 三、西门…

什么是DDoS流量清洗?

随着互联网的飞速发展&#xff0c;网络安全问题日益凸显&#xff0c;其中分布式拒绝服务&#xff08;DDoS&#xff09;攻击尤为引人关注。为了有效应对这一威胁&#xff0c;流量清洗服务应运而生&#xff0c;成为网络安全领域的一项重要技术。 流量清洗服务是一种专门针对DDoS…

Linux gurb2简介

文章目录 前言一、GRUB 2简介二、GRUB 2相关文件/文件夹2.1 /etc/default/grub文件2.2 /etc/grub.d/文件夹2.3 /boot/grub/grub.cfg文件 三、grubx64.efi参考资料 前言 简单来说&#xff0c;引导加载程序&#xff08;boot loader&#xff09;是计算机启动时运行的第一个软件程…

AtCoder ABC352 A-D题解

比赛链接:ABC352 Problem A: 签到题。 #include <bits/stdc.h> using namespace std; int main(){int N,X,Y,Z;cin>>N>>X>>Y>>Z;if((X<Z && Z<Y) || (Y<Z && Z<X))cout<<"YES"<<endl;else…

微服务雪崩问题、Sentinel(请求限流、线程隔离、服务熔断)、Seata分布式事务

文章目录 前言一、微服务保护二、Sentinel2.1 微服务整合2.2 簇点链路2.3 请求限流2.4 线程隔离2.5 服务熔断 三、分布式事务3.1 Seata3.1.1 Seata架构3.1.2 部署TC服务3.1.3 微服务集成Seata 3.2 XA模式3.3 AT模式 前言 微服务之间为什么会雪崩&#xff1f;怎么解决雪崩问题&…

使用 Supabase 的 Realtime + Storage 非常方便呢

文章目录 &#xff08;一&#xff09;Supabase&#xff08;二&#xff09;Realtime&#xff08;消息&#xff09;&#xff08;2.1&#xff09;Python 消息订阅&#xff08;2.2&#xff09;JavaScript 消息订阅 &#xff08;三&#xff09;Storage&#xff08;存储&#xff09;&…

如何撰写EI会议的投稿信?

撰写EI会议的投稿信&#xff08;Cover Letter&#xff09;是向会议组织者介绍你的论文和研究工作的一个重要环节。以下是撰写投稿信的一些关键步骤和建议&#xff1a; 投稿信的结构 信头 你的信息&#xff1a;包括姓名、职位、单位名称、通讯地址、电子邮件和电话号码。日期&am…

LeetCode198:打家劫舍

题目描述 你是一个专业的小偷&#xff0c;计划偷窃沿街的房屋。每间房内都藏有一定的现金&#xff0c;影响你偷窃的唯一制约因素就是相邻的房屋装有相互连通的防盗系统&#xff0c;如果两间相邻的房屋在同一晚上被小偷闯入&#xff0c;系统会自动报警。 给定一个代表每个房屋存…

【论文笔记】Layer-Wise Weight Decay for Deep Neural Networks

Abstract 本文为了提高深度神经网络的训练效率&#xff0c;提出了逐层权重衰减(layer-wise weight decay)。 本文方法通过逐层设置权重衰减稀疏的不同值&#xff0c;使反向传播梯度的尺度与权重衰减的尺度之比在整个网络中保持恒定。这种设置可以避免过拟合或欠拟合&#xff0…

完全匹配企业需求的替代FTP升级软件怎么找

企业在处理数据传输时&#xff0c;效率和安全性是关键。尽管传统的FTP曾被广泛采用&#xff0c;但因其传输慢、安全性不足和难以管理等问题&#xff0c;已不再满足现代企业的需求。许多企业正在寻找能够满足其需求的FTP替代方案&#xff0c;但市场上选择众多&#xff0c;找到合…

设计模式使用(成本扣除)

前言 名词解释 基础名词 订单金额&#xff1a;用户下单时支付的金额&#xff0c;这个最好理解 产品分成&#xff1a;也就是跟其他人合做以后我方能分到的金额&#xff0c;举个例子&#xff0c;比如用户订单金额是 100 块&#xff0c;我方的分成是 80%&#xff0c;那么也就是…

双向链表C++,C#,Java版,这些程序大多已经过测试,一直在用。

先C版吧&#xff0c;我最先用的是C#,后来是Java&#xff0c;后来改用C版的&#xff0c;因为现在一直在用C&#xff0c;单链 表一直没写上去&#xff0c;因为我很少用&#xff0c;用的是双链表。 执行代码例子1&#xff1a; int main() { _DList<_string> s…

小恐龙跳一跳源码

小恐龙跳一跳源码是前两年就火爆过一次的小游戏源码&#xff0c;不知怎么了今年有火爆了&#xff0c;所以今天就吧这个源码分享出来了&#xff01;有喜欢的直接下载就行&#xff0c;可以本地单机直接点击index.html进行运行&#xff0c;又或者放在虚拟机或者服务器上与朋友进行…

基于RV1126的AI网络摄像机AHD、CVBS、HDMI接口的区别有哪些?支持8路AHD摄像头,支持AI实时分析

网络摄像机AHD、CVBS、HDMI接口的区别有哪些&#xff1f;应用场景有哪些&#xff1f; AHD、CVBS和HDMI是不同的视频传输接口&#xff0c;分别适用于不同的应用场景。下面是它们的主要应用场景&#xff1a; 1.AHD&#xff08;Analog High Definition&#xff09;&#xff1a;A…