前端离不开网络请求,那有哪些方式可以发送请求呢?首先,在代码中使用XHR和fetch、构造Img等都是比较常用的手段,开发者工具也提供了重发请求的功能用于调试;类库方面,axios,request,node-fetch等都能做得很好,特别是axios,在日常工作中经常用到;另外,一些调试工具如fiddler、whistle等都有composer功能,可以用于构造并发送请求,postman则是专门的客户端请求代理软件。除此之外,linux层面其实有一个原生的客户端请求软件 —— curl(client URL)。
curl可以支持SMTP, FTP, POP3, HTTP, HTTPS
等多种协议,我们在此只讨论HTTP家族的协议。
为了测试curl发送的请求头和请求体,可以在本地启动一个回声服务,原样发回请求报文文本:
const http = require('http');
const server = http.createServer((req, res) => {
res.setHeader('set-cookie', 'mycookie=1234567');
res.write(`
from port: ${req.socket.remotePort}
request method: ${req.method}
request url: ${req.url}
${JSON.stringify(req.headers)}
`);
req.on('data', (chunk) => res.write(String(chunk)));
req.on('end', () => res.end());
});
server.listen(3001, () => console.log('server running on port 3001'));
简单用法
使用curl发送一个get请求:curl localhost:3001
可以得到返回结果:
from port: 63574
request method: GET
request url: /
{"host":"localhost:3001","user-agent":"curl/7.64.1","accept":"*/*"}
可见发送的请求会默认带上host, user-agent, accept
这几个请求头,那怎么增加请求头呢?可以用上下面这些选项:
-A --user-agent
指定user-agent头字段-b --cookie
增加cookie字段,如果值以@开头则会找到对应的cookie文件读取cookie(cookie由服务器设置,可以保存在本地,详见下方"输出"相关的内容)-e --referer
携带referer头字段-H --header
添加/更改/删除头字段
-H
遵循下面的使用规则:
# 添加/修改请求头
--header "user-agent:myagent/super1.0"
# 删除请求头
--header "user-agent:"
# 增加空请求头
--header "x-custom-header;"
重定向
有了上面的基础,让我们构造一个请求:curl -A "Mozilla/5.0 whatever" -i -H "X-my-option:000" www.qq.com
(此处使用了-i
打印出响应头), 可以在命令行得到如下输出结果:
HTTP/1.1 302 Moved Temporarily
Server: stgw/1.3.12.4_1.13.5
Date: Sat, 14 Nov 2020 12:58:19 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://www.qq.com/
<html>
<head><title>302 Found</title></head>
<body bgcolor="white">
<center><h1>302 Found</h1></center>
<hr><center>stgw/1.3.12.4_1.13.5</center>
</body>
</html>
curl默认使用http协议进行请求,访问http://www.qq.com
会返回一个302的重定向消息,但是curl默认并不会自动跟踪重定向指向的网络地址https://www.qq.com
,此时可以添加-L --location
选项开启自动跟踪重定向,但要注意的一点是,网络重定向可能形成环状跳转,比如urlA重定向到urlB,urlB再重定向回urlA,这种跳转会一直持续下去,造成资源的浪费,可以使用--max-redirs
选项指定链接最大可跳转次数。
加上重定向,再请求一次curl -A "Mozilla/5.0 whatever" -i -H "X-my-option:000" -L www.qq.com
命令行打印如下,可见成功进行了跳转。
HTTP/1.1 302 Moved Temporarily
Server: stgw/1.3.12.4_1.13.5
Date: Sat, 14 Nov 2020 13:01:56 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
Location: https://www.qq.com/
HTTP/2 200
date: Sat, 14 Nov 2020 13:01:56 GMT
content-type: text/html; charset=GB2312
server: squid/3.5.24
vary: Accept-Encoding
vary: Accept-Encoding
expires: Sat, 14 Nov 2020 13:02:56 GMT
cache-control: max-age=60
x-cache: HIT from shenzhen.qq.com
<...省略html文本>
发送post请求
上面讲的都是发送get请求,那怎么发post请求呢?curl中可以指定-d --data
选项添加post数据,如:curl -d "name=可莉" --data "age=8" http://localhost:3001/
, 命令行打印:
from port: 64823
request method: POST
request url: /
{"host":"localhost:3001","user-agent":"curl/7.64.1","accept":"*/*","content-length":"17","content-type":"application/x-www-form-urlencoded"}
name=可莉&age=8
可见data选项原封不动地发送了我们提供的数据,在数据条目之间添加了&
,但一般post请求是需要对字符进行编码处理的,我们可以使用--data-urlencode
选项代替--data
,比如curl --data-urlencode "name=可莉" --data "age=8" http://localhost:3001/
,请求体就会进行编码:
from port: 65459
request method: POST
request url: /
{"host":"localhost:3001","user-agent":"curl/7.64.1","accept":"*/*","content-length":"29","content-type":"application/x-www-form-urlencoded"}
name=%E5%8F%AF%E8%8E%89&age=8
如果想发送文件,就需要用到另外一个选项-F --form
,开启这个选项curl使用内置的一个type为multipart/form-data
的form表单模拟提交操作,如:curl -F name=romio -F profile=@myprofile http://localhost:3001/
from port: 49288
request method: POST
request url: /
{"host":"localhost:3001","user-agent":"curl/7.64.1","accept":"*/*","content-length":"356","content-type":"multipart/form-data; boundary=------------------------1db05266264c6354"}
--------------------------1db05266264c6354
Content-Disposition: form-data; name="name"
romio
--------------------------1db05266264c6354
Content-Disposition: form-data; name="profile"; filename="myprofile"
Content-Type: application/octet-stream
###oooo### <文本内容>
##o####o##
#o##o#o#o#
##o####o##
###oooo###
--------------------------1db05266264c6354--
需要注意的一点是-d
和-F
不能混用,原因是比较清晰的,-d
选项发送的content-type
是application/x-www-form-urlencoded
, 与后者不同。
输出
如何把返回结果保存为文件呢?
-o --output
指定输出文件地址,指定为-
输出到标准输出-O
解析目标url,自动命名保存,如http://a.c.com/a.html
保存为a.html
--create-dirs
如果输出文件的路径不存在则创建-c --cookie-jar
存储服务端返回的cookie, 指定为-
输出到标准输出-D --dump-header
指定存储响应头的文件
比如使用curl -o file -c baiducookie -b @baiducookie http://www.baidu.com
, 则请求对应地址并保存到file文件,cookie保存到baiducookie文件。
指定输出格式
有时并不需要获取对应url对应的文本内容,只需要获取状态码、content-type等信息,这需要指定-w --write-out
选项,该选项使用一个格式化字符串作为参数,如curl -w '%{http_code} %{content_type}' www.baidu.com
输出的末尾将打印200 text/html
,进一步,我们可以定义一个检测url是否可达的bash函数:
is_url_reachable() {
local RET_CODE=$(curl -o /dev/null -IsfLw '%{http_code}\n' --max-time 3 "$1" 2>&1)
if [ -n "$RET_CODE" ] && [ $(echo $RET_CODE | cut -c 1-1) = "2" ]; then
return 0
else
return 1
fi
}
配置
超时和重试
limit-rate
参数是/\d+[KMG]/i
的形式,可以限制网络速度为多少K/M/G每秒--connect-timeout
指定超时时间(s)-m --max-time
指定最大持续时间(s),包含此命令执行所有阶段的时间--retry
重试次数--retry-delay
重试时间间隔(s)
代理
--proxy-header
为代理添加请求头-p --proxy
指定代理
如给请求添加代理:curl -p 8.8.8.8 www.baidu.com
请求方式
-G --get
置换请求方式为get-I --head
置换请求方式为head
当请求中添加-d
之后,curl默认发post请求,可以添加-G
或-H
将请求类型置换为get请求,c此时-d
中的内容会以?<params>
的形式附加到url中,如curl -G --data-url "name=可莉" -d age=8 localhost:3001
打印结果是:
from port: 50660
request method: GET
request url: /?name=%E5%8F%AF%E8%8E%89&age=8
{"host":"localhost:3001","user-agent":"curl/7.64.1","accept":"*/*"}
输出控制
这里的选项一般用在bash脚本文件中,可以更好地控制curl的输出。
-# --progress-bar
显示进度条-f --fail
如果服务端错误则静默失败,并返回error code 22--fail-early
一般curl都是顺序请求给定的url,并返回最后一个请求的错误码,开启这个选项可以在curl失败后立即返回-s --silent
不显示错误信息和进度条, 一般配合-S --show-error
使用,使用这个选项会显示错误信息-v --verbose
显示通信细节
配置文件
使用-K --config <filename>
可以指定一个文件作为curl的配置文件,该配置文件每一行描述一条规则,使用url=<request_url>
指定请求的url地址,格式如下:
# curlfile
# 指定请求地址
url=http://www.baidu.com
# 开启重定向
location
# 设置user-agent请求头
user-agent="Mozilla/5.0 whatever"
# cookie文件
cookie=@baiducookie
# 保存cookie的文件
cookie-jar=baiducookie
# 最大请求时间
max-time=10
# 请求速度
limit-rate=80K
# 显示进度条
progress-bar
# 输出
output=/dev/null
可以在同一个文件中描述多条请求,描述下一条请求需要使用-: --next
选项,上一条请求中指定的选项将重置,比如在上面的文件后面增加如下内容,即可发送两次请求:
# --------------------------- #
# the next request
next
url="http://localhost:3001/"
从控制台的打印结果看,两个请求是串行发送的,即先发送第一个请求,然后发送第二个,curl默认进行串行请求,添加-Z --parallel
之后可以并行请求(7.66.0版本开始支持,查看版本curl --version
)。如果curl版本不支持,可以使用类似下面的操作代替:
echo "-A \"Mozilla/5.0 whatever\" -b @baiducookie -L --limit-rate 20k -o /dev/null www.baidu.com \n -o /dev/null --limit-rate 20k -L www.qq.com" | xargs -L 1 -P 2 curl
调试
使用--trace <file>
选项可以开启网络调试,它会将请求过程中传输的二进制数据、数据的描述、请求阶段描述等信息保存为文件,也可以指定file为-
输出到标准输出。
如使用curl --trace tracefile --location www.qq.com
,网络请求信息会被保存到tracefile中,从其中可以清晰地分辨出请求头、请求体、重定向、TLS握手等信息:
== Info: Trying 61.241.49.173...
== Info: TCP_NODELAY set
== Info: Connected to www.qq.com (127.0.0.1) port 80 (#0)
=> Send header, 74 bytes (0x4a) # 请求头
0000: 47 45 54 20 2f 20 48 54 54 50 2f 31 2e 31 0d 0a GET / HTTP/1.1..
0010: 48 6f 73 74 3a 20 77 77 77 2e 71 71 2e 63 6f 6d Host: www.qq.com
0020: 0d 0a 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 75 ..User-Agent: cu
0030: 72 6c 2f 37 2e 36 34 2e 31 0d 0a 41 63 63 65 70 rl/7.64.1..Accep
0040: 74 3a 20 2a 2f 2a 0d 0a 0d 0a t: */*....
<= Recv header, 32 bytes (0x20) # 响应头
0000: 48 54 54 50 2f 31 2e 31 20 33 30 32 20 4d 6f 76 HTTP/1.1 302 Mov
0010: 65 64 20 54 65 6d 70 6f 72 61 72 69 6c 79 0d 0a ed Temporarily..
<= Recv header, 30 bytes (0x1e)
0000: 53 65 72 76 65 72 3a 20 73 74 67 77 2f 31 2e 33 Server: stgw/1.3
0010: 2e 31 32 2e 34 5f ...
<= Recv header, 2 bytes (0x2) # 响应头结束
0000: 0d 0a ..
== Info: Ignoring the response-body
<= Recv data, 169 bytes (0xa9) # 获取body
0000: 3c 68 74 6d 6c 3e 0d 0a 3c 68 65 61 64 3e 3c 74 <html>..<head><t
0010: 69 74 6c 65 3e 33 30 32 20 46 6f 75 6e 64 3c 2f itle>302 Found</
0020: 74 69 74 6c 65 3e 3c 2f 68 65 61 64 3e 0d 0a 3c title></head>..<
0030: 62 6f 64 79 20 62 67 63 6f 6c 6f 72 3d 22 77 68 body bgcolor="wh
0040: 69 74 65 22 3e 0d 0a 3c 63 65 6e 74 65 72 3e 3c ite">..<center><
0050: 68 31 3e 33 30 32 20 46 6f 75 6e 64 3c 2f 68 31 h1>302 Found</h1
0060: 3e 3c 2f 63 65 6e 74 65 72 3e 0d 0a 3c 68 72 3e ></center>..<hr>
0070: 3c 63 65 6e 74 65 72 3e 73 74 67 77 2f 31 2e 33 <center>stgw/1.3
0080: 2e 31 32 2e 34 5f 31 2e 31 33 2e 35 3c 2f 63 65 .12.4_1.13.5</ce
0090: 6e 74 65 72 3e 0d 0a 3c 2f 62 6f 64 79 3e 0d 0a nter>..</body>..
00a0: 3c 2f 68 74 6d 6c 3e 0d 0a </html>..
== Info: Connection #0 to host www.qq.com left intact
== Info: Issue another request to this URL: 'https://www.qq.com/' # 重定向到这个链接
== Info: Trying 61.241.49.173...
== Info: TCP_NODELAY set # TCP连接
== Info: Connected to www.qq.com (127.0.0.1) port 443 (#1)
== Info: ALPN, offering h2 # 应用层协议协商
== Info: ALPN, offering http/1.1
== Info: successfully set certificate verify locations: # 检查网站证书
== Info: CAfile: /etc/ssl/cert.pem
CApath: none
# 下面是TLS握手的消息
== Info: TLSv1.2 (OUT), TLS handshake, Client hello (1): # 客户端发送hello消息, 包含客户端支持的 TLS 版本、支持的密码套件,以及称为“客户端随机数”的一串随机字节。
=> Send SSL data, 224 bytes (0xe0)
0000: 01 00 00 dc 03 03 e5 d8 07 6e ...
== Info: TLSv1.2 (IN), TLS handshake, Server hello (2): # 服务端回复hello消息,内含服务器选择的密码套件,以及“服务器随机数”,即由服务器生成的另一串随机字节。
<= Recv SSL data, 102 bytes (0x66)
0000: 02 00 00 62 03 03 b7 41 b9 d7 ...
== Info: TLSv1.2 (IN), TLS handshake, Certificate (11): # 服务端发送 SSL 证书
<= Recv SSL data, 5882 bytes (0x16fa)
0000: 0b 00 16 f6 00 16 f3 00 11 d3 30 82 11 cf 30 82 ..........0...0.
0010: 10 b7 a0 03 02 01 02 02 10 07 88 51 f4 87 ...
== Info: TLSv1.2 (IN), TLS handshake, Server key exchange (12): # 服务端发送经加密的算法公钥(Server Random),需要客户端用网站证书中的公钥解密
<= Recv SSL data, 333 bytes (0x14d)
0000: 0c 00 01 49 03 00 17 41 04 28 4a 76 3e ...
== Info: TLSv1.2 (IN), TLS handshake, Server finished (14): # 服务端Hello完成
<= Recv SSL data, 4 bytes (0x4)
0000: 0e 00 00 00 ....
== Info: TLSv1.2 (OUT), TLS handshake, Client key exchange (16): # 客户端发送经加密的算法公钥(Client Random),需要服务端使用客户端的公钥解密,之后服务端和客户端分别通过秘钥生成算法生成主秘钥和会话秘钥
=> Send SSL data, 70 bytes (0x46)
0000: 10 00 00 42 41 04 b9 7c 5b 07 08 4c 49 ...
== Info: TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1): # 客户端之后使用会话秘钥(client_write_key)加密通信
=> Send SSL data, 1 bytes (0x1)
0000: 01 .
== Info: TLSv1.2 (OUT), TLS handshake, Finished (20): # 客户端握手成功
=> Send SSL data, 16 bytes (0x10)
0000: 14 00 00 0c 08 0e f2 19 12 1b 47 ff df 4a 19 1d ..........G..J..
== Info: TLSv1.2 (IN), TLS change cipher, Change cipher spec (1): # 服务端之后使用会话秘钥(server_write_key)加密通信
<= Recv SSL data, 1 bytes (0x1)
0000: 01 .
== Info: TLSv1.2 (IN), TLS handshake, Finished (20): # 服务端握手成功
<= Recv SSL data, 16 bytes (0x10)
0000: 14 00 00 0c b1 18 18 41 8a 46 9f e3 07 2c 16 99 .......A.F...,..
# TLS握手成功,开始进行HTTP通信
== Info: SSL connection using TLSv1.2 / ECDHE-RSA-AES128-GCM-SHA256
== Info: ALPN, server accepted to use h2
== Info: Server certificate:
== Info: subject: C=CN; ST=Guangdong Province; L=Shenzhen; O=Shenzhen Tencent Computer Systems Company Limited; OU=R&D; CN=www.qq.com
== Info: start date: Jun 22 00:00:00 2020 GMT
== Info: expire date: Sep 22 12:00:00 2021 GMT
== Info: subjectAltName: host "www.qq.com" matched cert's "www.qq.com"
== Info: issuer: C=US; O=DigiCert Inc; OU=www.digicert.com; CN=Secure Site CA G2
== Info: SSL certificate verify ok.
== Info: Using HTTP2, server supports multi-use
== Info: Connection state changed (HTTP/2 confirmed)
== Info: Copying HTTP/2 data in stream buffer to connection buffer after upgrade: len=0
== Info: Using Stream ID: 1 (easy handle 0x7f93c800fc00)
=> Send header, 72 bytes (0x48) # 发送请求头
0000: 47 45 54 20 2f 20 48 54 54 50 2f 32 0d 0a 48 6f GET / HTTP/2..Ho
0010: 73 74 3a 20 77 77 77 2e 71 71 2e 63 6f 6d 0d 0a st: www.qq.com..
0020: 55 73 65 72 2d 41 67 65 6e 74 3a 20 63 75 72 6c User-Agent: curl
0030: 2f 37 2e 36 34 2e 31 0d 0a 41 63 63 65 70 74 3a /7.64.1..Accept:
0040: 20 2a 2f 2a 0d 0a 0d 0a */*....
== Info: Connection state changed (MAX_CONCURRENT_STREAMS == 128)! # 接收响应头和响应体
<= Recv header, 13 bytes (0xd)
0000: 48 54 54 50 2f 32 20 32 30 30 20 0d 0a HTTP/2 200 ..
<= Recv header, 37 bytes (0x25)
0000: 64 61 74 65 3a 20 53 75 6e 2c 20 31 35 20 4e 6f date: Sun, 15 No
0010: 76 20 32 30 32 ...
总结
本文分析了curl这个客户端请求软件的基础用法,它的功能还是很强大的,在诸如接口调试、调用机器人API、下载文件(推荐直接使用wget
)等场合都可以发挥一些作用,写一个curl配置文件对定时任务也很有帮助,调试选项可以提供类似抓包工具的请求细节,对于分析、诊断网络请求有一定的作用。
更多选项请查看在线文档。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!