补充:URL 是统一资源定位符,见名知义,因为要定位,所以要指定协议甚至是位置,比如这样:http://localhost:5000/api/hello
HTTP/1.1
完整的第一行如下:
POST /api/hello HTTP/1.1
第二行的 User-Agent 则用于告诉对方发起请求的客户端是啥,比如咱们用 Postman 发起的请求,Postman 就会自动把这个参数设置为它自己:
User-Agent: PostmanRuntime/7.28.4
第三行的 Accept 用于告诉对方我们希望收到什么类型的数据,这里默认是能接受所有类型的数据:
Accept: */*
第四行就非常值得留意,Postman-Token
是 Postman 自己传的参数,这个我们放到下面讲!
Postman-Token: ddd72e1a-0d63-4bad-a18e-22e38a5de3fc
第五行是请求的主机,网络上的一个服务一般用 ip 加端口作为唯一标识:
Host: 127.0.0.1:5000
第六行指定的是咱们请求发起方可以理解的压缩方式:
Accept-Encoding: gzip, deflate, br
第七行告诉对方处理完当前请求后不要关闭连接:
Connection: keep-alive
第八行告诉对方咱们请求体的内容格式,这个是本文的侧重点啦!比如我们这里指定的是一般浏览器的原生表单格式:
Content-Type: application/x-www-form-urlencoded
前端面试 用前端面试题库 MST题宝库
好了,下面大家要留意了,第九行的 Content-Length 给出的是请求体的大小。
而请求体,会放在紧跟着的一个空行之后。比如本请求的请求体内容是以 key=value
形式填充的,也就是我们表单参数的内容了:
Content-Length: 23name=%E9%98%BF%E8%8F%8C
看到这里我们先简单小结一下,想要告诉服务器我们发送的是表单数据,一共需要两步:
将Content-Type
设置为 application/x-www-form-urlencoded
在请求体中按照 key=value
的形式填写请求参数 什么是协议?进一步了解 http
好了,接下来我们进一步讲解,大家试想一下,网络应用,其实就是端到端的交互,最常见的就是服务端和客户端交互模型:客户端发一些参数数据给服务端,通过这些参数数据告诉服务端它想得到什么或想干什么,服务端根据客户端传递的参数数据作出处理。
传输层协议通过 ip 和端口号帮我们定位到了具体的服务应用,具体怎么交互是由我们程序员自己定义的。
大概在 30 年前,英国计算机科学家蒂姆·伯纳斯-李定义了原始超级文本传输协议(HTTP),后续我们的 web 应用大都延续采用了他定义的这套标准,当然这套标准也在不断地进行迭代。
许多文献资料会把 http 协议描述得比较晦涩,加上协议这个词听起来有点高大上,初学者入门学习的时候往往感觉不太友好。
其实协议说白了就是一种格式,就好比我们写书信,约定要先顶格写个敬爱的 xxx,然后写个你好,然后换一个段落再写正文,可能最后还得加上日期署名等等。
我们只要按照格式写信,老师就能一眼看出来我们在写信;只要我们按协议格式发请求数据,服务器就能一眼看出来我们想要得到什么或想干什么。
当然,老师是因为老早就学过书信格式,所以他才能看懂书信格式;服务端程序也一样,我们要预先编写好 http 协议的解析逻辑,然后我们的服务器才能根据解析逻辑去获取一个 http 请求中的各种东西。
当然这个解析 http 协议的逻辑不是谁都能写出来的,就算能写出来,也未必写得好,所以我们会使用厉害的人封装好的脚手架,比如 java 里的 spring 全套、Go 语言里的 Gin 等等。
回到我们开头给出的示例:
from flask import Flaskfrom flask import requestapp = Flask(__name__)# 云你好服务 API 接口@app.get("/api/hello")def hello(): # 看用户是否传递了参数 name name = request.args.get("name", "") # 如果传了参数就向目标对象打招呼,输出 Hello XXX,否则输出 Hello World return f"Hello {name}" if name else "Hello World"# 启动云你好服务if __name__ == '__main__': app.run()
阿菌的示例使用了 python 里的 flask 框架,在处理逻辑中使用了 request.args 获取请求参数,而 args 封装的就是框架从 url 中获取参数的逻辑。比如我们发送请求的 url 为:
http://127.0.0.1:5000/api/hello?name=ajun
框架会帮助我们从 url 中的 ? 后面开始截取,然后把 name=ajun
这些参数存放到 args 里。
切换一下,假设我们是云你好服务提供者,我们希望用户通过表单参数的形式使用云你好服务,我们只要把获取 name 参数的方式改成从表单参数里获取就可以了,flask 在 request.form 里封装了表单参数(关于框架是怎么在数行 http 请求中封装参数的,大家可以看自己使用的框架的具体逻辑,估计区别不大,只是存在一些语言特性上的差异):
@app.post("/api/hello")def hello(): # 看用户是否传递了参数 name name = request.form.get("name", "") # 如果传了参数就向目标对象打招呼,输出 Hello XXX,否则输出 Hello World return f"Hello {name}" if name else "Hello World"
思考:我们可以在 http 协议中传递什么参数?
最后,我们解释本文的标题,其实想要明白各种参数之间的区别,我们可以换一个角度思考:
咱们可以在一份 http 报文的哪些位置传递参数?
前端面试 用前端面试题库 MST题宝库
接下来回顾一下一个 http 请求的内容:
POST /api/hello HTTP/1.1User-Agent: PostmanRuntime/7.28.4Accept: */*Postman-Token: fbf75035-a647-46dc-adc0-333751a9399eHost: 127.0.0.1:5000Accept-Encoding: gzip, deflate, brConnection: keep-aliveContent-Type: application/x-www-form-urlencodedContent-Length: 23name=%E9%98%BF%E8%8F%8C
大家看,咱们的 http 报文,也就是基于传输层之上的应用层报文,大概就长上面这样。
我们考虑两种情况,第一种情况,我们基于别人已经开发好的脚手架开发 http 服务器。
由于框架会基于 http 协议进行解析,所以框架会帮助我们解析好请求 url,各种 Header 头(比如:Cookie 等),以及具体的响应内容都帮我们封装解析好了(比如按照 key=value 的方式去读取请求体)。
对象篇
模块化编程-自研模块加载器
开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】