如何通过命令行发起 HTTP 请求?
细想一下,相信这种需求场景,其实对于我们(这里当然是指程序员们)的日常工作十分常见。举个例子,比如需要测试服务端同学提供的 API 是否正常服务;当然也有比如说想通过 HTTP 服务上传、下载某个文件;跨端传输文件;接口测试:开启静态资源服务,诸如此类等等。
所以,今天就探索以及分享一下命令行下折腾 HTTP 服务、请求、代理等的操作。
当然也有同学可能会说可以用浏览器、Postman 等等。这里并不否认图形应用软件的便利之处,但是命令行也有它的优点,更加简单便捷,接近底层,同时表达力丰富等。
首先,为了做实验,我们需要一个 HTTP 服务器。本文就使用
github.com/typicode/js…
迅速搭建了一个 REST API 的服务器。(搭建流程参考该仓库的 README,30s 内即可搭建成功)
准备工作就是这么简单。正文开始。
首先要介绍的是 cURL,一般系统上都会预装。这个工具主要作用是用来上传和下载 URL 指定的数据。它的命名来由摘抄如下:
它会显示数据(默认情况下),用户可以看到 (see)URL,并且 see 可以简写为单个字母 c。此外,它是一个客户端程序,一个 URL 客户端,字母 c 也可以表示客户端(client),因此就有了 cURL 这个名字。
但是也有人指出是“递归缩写” —— Curl URL Request Library。就和 PHP(PHP:Hypertext Preprocessor)一样,也挺有趣的。
cURL 是一个很强大的工具。关于 cURL 使用姿势之丰富都可以专门写一本书,互联网上介绍它的文章资料也多如牛毛。所以,这里当然就只介绍与本文宗旨相关的常用操作。
cURL 的命令选项多达 200 个以上,除了使用 man curl 之外,还可以通过在线手册查询:
curl.se/docs/manpag…
-O, --remote-name选项代表着将响应输出写入和远程服务上的文件名一样的本地文件上。
curl -O 127.0.0.1:3000/posts
效果如下图,终端会显示一个进度指示器向用户展示进度详情。
灵活一些的话,可以使用 -o, --output 选项将输出写入文件中,而不是标准输出流(stdout)
curl -o posts 127.0.0.1:3000/posts
当然也等价于使用重定向输出的技巧将 curl 标准输出流定向到任意文件中
curl 127.0.0.1:3000/posts > posts
如果需要同时下载多个资源,也很简单,可以写在一起,如下:
curl \
-o file1 127.0.0.1:3000/posts \
-o file2 127.0.0.1:3000/comments
当然 curl 还有许多其他下载相关的特性,比如限制下载速度与尺寸、失败重试、设置偏移量继续下载、指定下载范围等等。这个感兴趣都可以去探索探索。
详细模式与静默模式
如果需要展示请求响应的详细信息,可以添加 -v, --verbose 选项。一般适用于调试时使用。
curl -v 127.0.0.1:3000/posts
如下图所示,可以划分为以下几个区域。
开头的几行,表示了 cURL 尝试并成功连接了 IP 地址(127.0.0.1:3000)。
curl -i 127.0.0.1:3000/posts
与之相对应的就是静默模式 -s, --silent。比如如下组合就可以让 curl 下载资源时不打印任何进度信息。
curl -sO 127.0.0.1:3000/posts
cURL 在默认情况下是 GET 请求。那么如何使用 POST、HEAD、PUT 请求呢? curl 不会在命令行中指定方法。一般情况下,不同请求会有对应参数指示 curl 使用对应方法。
如果使用了 -d, --data 或者 -F, --form 参数,则默认使用 POST 请求。示例如下:
curl -v 127.0.0.1:3000/posts -d title=fake-server -d author=sulirc
注意请求头:POST /posts HTTP/1.1 证明了 cURL 确实使用了 POST 请求。
当然如果想写简单点,可以用浏览器“URL编码”形式发送,如下:
curl -v 127.0.0.1:3000/posts -d 'title=express-server&author=david'
或者,如果参数的数量极大,也可以放在文件内,使用如下方式从文件中读取参数:
curl -v 127.0.0.1:3000/posts -d @data-file
同样的,-I 对应 HEAD 方法。-T 对应 PUT 方法。这个不详细讲了。
设置请求头
我们有没有留意到,上述的请求中:Content-Type: application/x-www-form-urlencoded。那如果我们想使用 JSON 格式请求服务器。那么则需要用到设置请求头的能力,参数:-H, --header。
curl -v -d '{"title":"mock-server","author":"christina"}' -H 'Content-Type: application/json' 127.0.0.1:3000/posts
示例如下:
关于请求,cURL 还有丰富的选项参数。大家若有需要,可以阅读手册进行大概了解。
值得留意的是,一般现代浏览器比如 Chrome、Safari、Firefox,以及一些代理工具比如 Charles 都提供了拷贝请求为 cURL 的选项,十分便利。
HTTP Cookie
基于 HTTP 无状态的特性(或者说不足之处),HTTP Cookie 被发明为保持请求间的状态。而 cURL 秉承着做最少的事情的原则之下,一般是默认不使用 Cookie 的。那如何使用呢?
一般我们知道,基于 Cookie 的技术主要是浏览器在使用。浏览器会在 Application 里管理 Cookies。
为了方便实验,简单的搭建了一个设置并回显两个 Cookie 的 Koa 服务器。代码简略如下:
const createId = () => Math.random().toString(16).slice(2).toUpperCase();
app.use(async (ctx) => {
if (ctx.get("cookie")) {
ctx.body = ctx.get("cookie");
} else {
ctx.cookies.set("track-id", createId());
ctx.cookies.set("token", "#secret token#");
ctx.body = "no-cookie";
app.listen(PORT, () => {
console.log(`My server running, listening port ${PORT}`);
记录 Cookies
在命令行中,则需要将 Cookies 写入文件。参数为:-c, --cookie-jar
curl -v -c cookie.txt 127.0.0.1:3000
示例如下:
须知,第一次访问时,客户端(在这里也即是终端)并无本地的 Cookies。从请求头可知。通过运行命令后,响应头中明显出现了 Set-Cookie 字段,参见上图蓝色高亮区域。同时本地也多了一个 cookie.txt 文件(curl 使用的 cookie 文件格式叫作 Netscape cookie 格式,可以与浏览器适配)。
使用 Cookies
因此,下次请求时,可以通过 -b, --cookie <data|filename> 参数进行设置。
curl -v -b cookie.txt 127.0.0.1:3000
示例如下:
图中蓝色高亮区域即是代表已经正确读取,并从请求头中携带给了服务器,最终也可以看到响应数据中已经回显 Cookies。
销毁会话 Cookies
一般在浏览器中,重启时都会销毁所有的会话 Cookies。那在 cURL 中如何同样模拟一样的行为呢?使用 -j, --junk-session-cookies 参数让 cURL 开始新的会话。
curl -v -j -b cookie.txt 127.0.0.1:3000
如下图:
可以看到,即使有 cookie.txt 存在,cURL 也并没有选择发送,而服务器也重新发送了新的 Set-Cookie 响应头指示客户端更新 Cookies(track-id 已更新)。
HTTPie
仓库:github.com/httpie/http…
httpie 是一个特别好用的工具,官方介绍如下:
HTTPie (pronounced aitch-tee-tee-pie) is a command-line HTTP client. Its goal is to make CLI interaction with web services as human-friendly as possible. HTTPie is designed for testing, debugging, and generally interacting with APIs & HTTP servers. The http & https commands allow for creating and sending arbitrary HTTP requests. They use simple and natural syntax and provide formatted and colorized output.
简单来说,我们可以理解为 cURL 的更友好的替代品。cURL 常用的一些操作,httpie 一般也能覆盖到,但是在终端显示的更友好、更易读。
比如简单的本地服务器(localhost)的 GET 请求,我们可以省略 ip,示例如下:
终端输出的是着色后的 http 响应头、格式化良好的响应数据。对比 cURL 确实显得眼前一亮。
如何下载资源呢?重定向肯定管用。
http :3000/posts > posts.json
如果想要指定下载到某个文件中。可以使用 --output, -o 选项参数。
http :3000/posts -o my_posts.json
上述两个命令都不会打印任何信息。如果想得知进度以及响应头。可以使用 --download, -d 参数。
http :3000/posts -d -o my_posts.json
示例如下:
详细模式与静默模式
httpie 一般只展示响应头,如果同时也想要展示请求详情。
http -v :3000/posts
留意下述请求头和响应头有空行进行分割(可参照 HTTP 协议)
如果想要静默模式,可以使用 --quiet 选项。这样所有的标准输出流和标准错误流都会被重定向到 /dev/null 中。但是并不影响 -d -o 上述介绍的选项,也就是说仍然会正常下载文件。
在 httpie 里请求方法的使用方式非常明确。一般在缺省情况下,也是默认使用 GET 方法,如需指定其他方法,HTTP 方法就直接放在 URL 正前方即可。
因此比如使用 POST 方法,即可以如下使用:
http -v POST :3000/posts title=mock-server author=ygj
示例图如下:
当然,对于 PUT、DELETE 这些请求也是一样,但是由于一般工作中少有使用,也就不展示示例了。
不知道同学们有没有注意到,对比上文中的 cURL 进行 POST 请求,除了展示格式之外,还有一些细微之处不同。httpie 在 POST 请求加入 data 参数的情况下,默认使用了 Content-Type: application/json 请求头上传参数。
如果想使用 Content-Type: application/x-www-form-urlencoded 传输数据,可以使用 -f, --form 参数。
http -v -f POST :3000/posts title=mock-server author=ygj
示例如下:
当需要上传复杂的数据时,和 cURL 一样都可以使用 @ 符号引用文件。如下:
http -v POST :3000/posts @posts.json
示例如下:
设置请求头
当然,作为一个客户端,发起一个 http 请求时,必然需要有定制 http 请求头的能力。在 httpie 里也十分简单。比如我想要自定义一个请求头,可以很自然的书写如下:
http -v :3000/profile 'X-Demo-Time: 2021/01/03'
示例图:
单引号是为了防止 shell 自动展开的特性而写的。如果你很确定书写的请求头中不会有展开的风险,则可以不加单引号。比如这种模式就不需要:
http -v :3000/profile X-Demo-Author:sulirc
HTTP Cookie
上文我们介绍了 cURL 中 Cookie 的处理。 同样的操作 HTTPie 中也有。这里复用上文中的 Koa 服务器进行示例演示。
当我们第一次请求时,合乎预期的显示如下:
使用 Cookies
HTTPie 中提供了写 HTTP 请求头的方式去写 Cookies。手动拷贝多个 Cookies,注意以 ; 分隔:
http -v :3000 'Cookie:track-id=CC649CF69E3E9;token=#secret token#;'
示例如下:
但是手动管理的方式多么不方便!
如果我需要保持一个会话 Cookies 来复用请求的话。HTTPie 其实也提供了专门的 Sessions 管理方案。
写入命名 Session
可以通过 --session 选项将会话 Cookies 写入本地 session.json 文件。
http -v --session=./session.json :3000
session.json 文件存储格式如下,可以发现和上文 cURL 的 cookie.txt 格式完全不一致,这是不同客户端对应的不同实现:
我们也可以更具体的命名 session。
http -v --session=david-session.json :3000
下面图例展示了开启并存储了两个不同的会话 Cookies。
使用 Session
使用起来也十分直观,和写入命名 Session 一致。代表复用此 Session。
http -v --session=david-session.json :3000
此外还有创建匿名、只读的 Session。这里就不详细展开讲了。可以查阅官方文档。
下面这个表格截图自官方文档,大家可以仔细阅读一下,以下参数使用再配合配合 HTTP 方法进行使用,基本上,httpie 可以完成许多事情了。
关于 httpie 的详情请参阅官方文档:httpie.io/docs。除了上文介绍的基本特性之外,还有代理、SSL、离线模式等特性可以去探索(如有需要的话)。
它明显的缺点就是不是系统自身自带的工具,需要额外安装,不像 cURL 一样是工程师们熟悉的工具以及事实标准(也就是说,不能默认别的同事会这个工具)。功能也当然没有 cURL 那么强大,但是在日常工作中,其实一般来说够用,同时还可以有一个更美观、易用的 http 命令行工具选择,提升自己的工作体验,也是一件很值得尝试的事情。
http-server
仓库:github.com/http-party/…
讲完 cURL 和 httpie,提起文件下载与传输,我也很爱用这个简单实用的一键搭建服务器的方式。它的简单之处就在于,只用在当前文件目录敲入 http-server 命令即可(默认 8080 端口):
http-server ./
示例如下:
当然有同学会问,搭建一个静态资源服务器有什么用?简单来说,就是建立了一种能力:将本地文件系统与互联网连接了起来。而比较接地气的说法,就是可以把自己电脑的文件快速通过 HTTP 服务分享给其他同事,或者手机,在某些场景下特别有用。
http-server 还有一些其他参数,比如设置 SSL、端口、跨域等,如有需要,可自行查阅。
如果单纯的需要文件传输功能,推荐加密、跨平台、多文件传输 CLI 工具 croc:github.com/schollz/cro…。
其实 bash/shell 是可以学习十几年的东西,它的优美之处在于,它是属于那种,会的东西越多,你能组合发挥出来的魔力也就越大,同时它也是接近于较底层的工具,学习探索它的过程,也是在学习操作系统的过程。
今天这篇博客希望大家会喜欢,也希望能对日常的工作有帮助。
以上,感谢阅读。
curl.se/docs/manpag…
github.com/http-party/…
httpie.io/docs
docs.mitmproxy.org/stable/