什么是HTTP
http全称是"超文本传输协议"是一种应用非常广泛的应用层协议。http是基于于TCP协议实现的(http1.0,http1.1,http2.0均以tcp实现,http3.0基于udp实现),目前我们主要使用http1.1和http2.0,本篇文章以http1.1为主。
超文本协议:传输的内容不仅仅是文本(html,css这些是文本)还可以是一些其他资源,如图片,视频,音频等二进制数据。
咱们平时打开一个网站,就是通过http协议传输数据。例如咱们在浏览器输入百度网址http://www.baidu.com
,浏览器就该百度的服务器发送一个http请求,百度的服务器返回一个http响应。这个响应被浏览器解析以后,就展示出我们看到的页面。(这个过程中浏览器可能给服务器发送多个http请求,服务器会对应返回多个响应,这些响应中包含了页面html,css,js,图片,字体等信息)
http的报文格式
咱们可以借助抓包工具,如fiddler来分析http协议格式。
- http请求,主要由 首行,请求头,空行,正文body组成
- 首行:主要有方法,url,版本号三部分
- 请求头header:有很多键值对组成,每一行一个键值对,使用
:
来分割键和值 - 空行:header的结束标志
- 正文body:承载一些具体的数据,可能是json格式的,也可能是其他格式
- http响应,主要由 首行,响应头,空行,正文body组成
- 首行:主要由版本号,状态码,状态码分析组成
- 响应头header:有多个键值对,每一行一个键值对,使用
:
来分割键和值 - 空行:header的结束标志
- 正文body:承载一些具体数据,可能是json格式,也可能是html,css,js,图片等
HTTP请求
认识URL
url就是我们俗称的"网址",是唯一资源定位符(用来找到网络上的资源)。另外一个相近的概念的是uri,唯一资源标识符(用来区分一个网络上的资源)
- 协议方案名:url并不是http独有的,所以url前头要标识是用哪种协议
- 登入信息:现在基本没有这个了
- 服务器地址:就是服务器的ip地址,也可以是域名
域名和Ip地址是等价关系,域名要经过dns解析,帮我们自动把域名转换成Ip地址
- 服务器端口号:标识要访问目标服务器的哪个进程
ip+port就能确定是互联网上的哪个主机的哪个进程
- 带层次的文件路径:服务器进程可能会提供出很多资源(比如html文件,css文件,图片等)放到一些具体目录中。咱们就需要指定要访问的路径,才能获得资源。
url中写的路径,不一定完全和服务器上的某个硬盘上的目录一一对应。例如服务器上的目录是
D:/source/index.html
,把D:/source
这个目录映射成http的跟目录/
,因此要访问index.html
文件,就要写作http://127.0.0.1:80/index.html
.上面
index.html
是一个真实存在的文件,服务器也可以提供"虚拟出来的文件"。例如,可以在http服务器中,加入一些特殊逻辑,比如请求的url的路径是/test
,在http服务器中,直接在内存中构造出一个html,返回给浏览器,此时并没有访问硬盘的真实文件,而是直接在内存中拼一个html出来
- 查询字符串:
?key1=value1&key2=value2
查询字符串(query string),浏览器给服务器传递一些必要参数,这些键值对是由业务决定的,完成由程序员自定义。例如http://xxx大学2餐厅:6号窗口/骨汤麻辣烫?口味=微辣&是否放香菜=放&是否放葱=放
.个性化需求是由查询字符串传递的。 - 片段标识符:用来区分一个网页中哪个部分,常见于小说网站或者文档网站,借助片段标识符快速跳转到网页的某个部分。
URL的encode/decode
url中可能包含一些特殊含义的符号,如/ ? @等
,万一query string的value中,也包含这些特殊字符,可能导致浏览器错误识别url,服务器也可能错误的解析url.(类似于变量名不能是关键字)。因此需要对特殊字符进行转义,还包括汉字。
转义规则:把待转义的字符串的每个字符按照16进制表示,每个字节前面加上%
。
具体可以查看这个网站:encode网站
认识方法
简单来说可以把方法理解成你这个请求想做什么
在咱们开发中,主要使用get和post方法,因此这里只介绍这两个
get方法
常用get请求的场景
- 浏览器地址栏中直接输入url,会触发get请求
- html中的
link
,a
,img
,script
会触发get请求 - form表单
- ajax
如何传递信息
如果需要给服务器传递一些信息,在get请求中,通常通过querystring来传递。
post请求
常用post请求的场景
- form表单
- ajax
如何传递信息
在post请求中url通常是没有query string的,在传递信息给服务器,通常把消息放到body中
get和post的区别
- get和post没有本质区别,使用get实现的场景,基本可以使用post代替;使用post实现的场景,也可以使用get代替
- get的语义,是"从服务器获取数据",而post的语义,是"往服务器上提交数据"
- 从使用习惯上,给服务器传递的数据,get通常放在url的query string中;而post通常放到body中。(get请求可以把数据放到body中,post请求也可以把数据放到query string中,只是少见)
- get请求建议实现成"幂等",而post一般不要求实现成"幂等"。设计服务器的时候,需要提供一些"接口/api",api传入的参数,就是输入;api返回的结果,就是输出。基于get的api一般建议设计成幂等的,基于post的api则无要求。
幂等:输入是确定的,输出结果也是确定的。
- 在幂等基础上,get的请求结果可以被缓存(幂等的结果是确定的),而post则一般不会被缓存。
当然有的get请求不是幂等,因此要通过特殊的技巧不让浏览器产生缓存,典型做法就是让每次get请求的url都不相同,可以通过quert string来保证url不同
认识请求报头"header"
请求头中的键值对,都是标准规定的,有特定含义。
这里以访问百度网站为例www.baidu.com
,进行抓包
host
表示服务器主机的地址和端口,确定是互联网上哪个主机上的哪个进程。
这里是域名,域名经过dns解析后就是ip。此处没有显示port,表示默认。http的默认端口是80,https的默认端口是443.
问url中也有这个ip+port,为什么还要有Host?
url中的Ip和端口,和host里的ip和端口,不一定完全相同。当请求是经过代理来访问的时候,有可能不一样。
content-length和content-type
- content-length:表示body中的数据长度
- contetn-type:表示body中的数据格式
如果是get请求,没有body,就没有这两个字段。如果是post请求,有body一定有这两个字段。
为什么需要content-type?
body中的数据类型有很多种格式,对于接收方来说,采取的解析方式是不同的。
为什么需要content-length?
http是基于tcp的,而tcp是面向字节流,存在粘包问题。解决方法有两种1. 约定一个分割符,也就是header和body之间的空行;2. 约定报文长度
有哪些格式?
application/x-www-form-urlencoded
,像html的form表单构造的请求,使用的就是这个格式。
格式和query string一样,可以放很多键值对,键值对之间用&分割,键和值之间用=分割。例如title=test&content=hello
,并且这里也需要urlencode。
multipart/form-data
该格式主要用于上传文件
application/json
数据为json格式,body格式如下
User-Agent(UA)
UA主要是浏览器信息和操作系统信息,描述了用户用什么设备上网
早期的浏览器,只支持文字,后来开始支持图片,再后来支持js,后面支持音频。在同一时间段内,用户使用多种浏览器。因此开发者就利用收集到的浏览器/操作系统信息,个性化的返回不同的页面。UA的作用大大降低
随着时间推移,浏览器都趋于一致,开发者不再需要纠结浏览器兼容问题。而现在设备主要有PC,平板,手机需要开发出不同版本的页面,后面又推出"响应式页面"能感知浏览器窗口大小,根据不同尺寸,重新排列布局。UA的作用又被削弱了
而现在UA主要用来在服务器端统计用户的设备情况
Referer
Referer指的是当前页面,是从哪个页面跳转过来的。所以直接在浏览器地址输入地址,没有referer;点收藏夹,也没有referer。
例如在百度上投放广告,采用点击计费(CPC广告)和广告主结算广告费。统计点击次数时,百度自己会统计,广告主也会统计服务器中收到的请求,有多少referer来自百度的。
Cookie
cookie也是请求头的一个重要字段,是浏览器在本地存储数据的一种机制,但是只能存一些简单的键值对信息,比如存上次访问页面的时候,当前页面的访问次数,当前访问页面的身份信息。
浏览器为了安全,禁止网页js访问电脑的硬盘,浏览器专门提供了特殊的api给网页使用,可以让网页存储一些简单的数据。浏览器提供持久化存储的方案,有好几种,例如:cookie最经典的一种方案,LocalStorage和indexDB是比较新的方案。
cookie从哪来
cookie存在于浏览器,来源于服务器。
像上图,cookie是咱们浏览器访问了bing服务器之后,服务器返回的响应报文中,在响应header中包含一个或多个set-cookie这样的资源。浏览器看到set-cookie就会把这样的数据保存到浏览器本地。
cookie到哪去
cookie来自于服务器,存储于浏览器,还要再回到服务器。当浏览器保存了cookie之后,下次浏览器访问同一个网站,就会把之前本地存储的cookie,通过http请求的header的cookie带过去。
cookie的典型应用场景
cookie最常用的场景就是客服端维持登陆状态。
在某个网站上登陆成功后,浏览器就会记住当前登入用户的身份信息。然后接下来访问网站的其他页面时,服务器也能知道是谁在登入。
HTTP响应
状态码
介绍几个常见的状态码
- 200:表示访问成功
- 404:not found,说明请求路径写错了,请求是根据url的路径去访问服务器上的资源,如果想访问的资源,服务器没有,就会返回404
- 403:forbidden,访问被拒绝(没有权限)
- 500:Internal Server Error,服务器内部错误,服务器代码执行过程中,出现异常。
- 504:Gateway Timeout,访问超时,一般是服务器请求量很大,对于服务器的负荷比较重
- 302:重定向,访问一个旧的url自动转移到新的url上。例如服务器的地址迁移,在访问老网站时会自动帮你跳转到新网站。
1开头的表示等等
2开头的表示正常访问
3开头的表示重定向
4开头的表示客服端出现问题
5开头的表示服务端出现问题
Content-type
响应中常见的content-type
- text/html:body的格式是html
- text/css:body的格式是css
- application/javascrtpt:body的格式是js
- application/json:body的格式是json
构造HTTP请求
- 浏览器地址栏中输入url,构造get请求;点击a标签,构造get请求;img,link,script也会构造get请求
- form表单构造get/post请求,此时的content-type是
x-www-form-urlencoded
html
<form action="https://www.baidu.com" method="get">
<!-- 借助下面的标签给请求构造数据,每个input对应一个键值对,
name属性为key,input中用户输入的内容就是值
如果是get方法,上述键值对就会出现url的query string中
如果方法是post,上述键值对就会放到body中(格式和query string相同)-->
<input type="text" name="username">
<input type="password" name="password">
<input type="submit" value="提交">
</form>
html
<form action="https://www.baidu.com" method="post">
<input type="text" name="username"> <br>
<input type="password" name="password"> <br>
<input type="submit" value="提交">
</form>
- ajax 各种http方法都能构造
form构造的http请求一定会触发页面跳转,而ajax默认发起的请求不会引起跳转,可以达到"局部刷新"的效果
ajax的全称是Asynchronous JavaScript And Xml
。
- Asynchronous表示异步,在网络通信/IO操作中的异步表示:谁发起的请求,谁负责接收是同步,异步和同步相对,表示发起请求的主体,不负责接收结果,而是别人主动推送过来。
类似于餐馆吃饭,点完单以后,如果煮好了,自己去端是同步,等服务员帮你端过来是异步
html
<!-- 引入第三方库,jquery -->
<script src="../jquery-1.12.4.js"></script>
<script>
//$是jquery的全局变量,通过这个对象,可以调用里面的一些方法
$.ajax({
// 访问地址
url: "https://www.baidu.com",
type: "post", //访问方法
data: "http请求的body", // 传给后端的数据
contentType: 'x-www-form-urlencoded', // 构造http请求的方式
// 后端给前端推送的结果,异步的表现
// 使用回调函数,根据返回结果的不同执行不同的方案
success:function(body) {
console.log("成功")
},
error:function(body) {
console.log("失败")
}
})
</script>