HTTP协议-AJAX

客户端和服务器

客户端和服务器是什么

  1. 在网络中,两个应用程序之间会发生通信,在绝大多数的情况下,这个通信总是由一发发出一个消息开始,另一方回复一个消息结束
  2. 发出消息的一方为客户端(Client),发出消息的过程称之为请求(Request)
  3. 回复消息的一方为服务器(Server),回复消息的过程称之为响应(Response)
  4. 因此在一次通信中,谁发出消息谁就是客户端,反之则是服务器,当然,大部分情况下,发出消息的一方都是浏览器

扩展

  1. 在我们软件开发中,不管是客户端还是服务器都只是一个应用程序,也包括数据库,数据库本质也只是一个持久化存储的软件
  2. 客户端和服务器可以在不同的计算机上,也可以在同一台计算机上
  3. 客户端和服务端的交互模式中我们称之为C/S结构,但是如果在这个交互中,客户端是浏览器,我们称之为B/S结构
  4. 服务器往往是在互联网的产品提供服务,所以我们一般把服务器称之为 web服务器

URL

什么是 URL

  1. 了解了客户端和服务器之后,我们可以来思考一件事情,这个客户端和服务器是在交互,但是客户端怎么就可以找到服务器呢?

  2. 在生活中,我们网购下单的时候就相当于一次请求,快递发到家里就是一次响应,那么快递是通过什么找到你家的,是不是就是我们填写的地址啊,比如:xx省xx市xx区xx小区xx单元xx号

  3. 那么在客户端和服务器的一次通信当中呢,也存在着这样的一个地址,就是 URL 地址,全称是(Uniform Resource Locator),也叫作全球资源定位器

  4. URL 是一种标准格式的字符串,通过这个字符串就可以找到资源在互联网上的地址,比如:音乐、图片、文件、视频等等

  5. 一个完整的 URL 地址组成部分如下:

    URL = 协议 + 主机 + 端口 + 路径 + 参数 + hash
    
  6. 示例:

    http://www.coderjc.com:8080/api/books?page=0&offset=8#main
    	- http【协议】
    	- www.coderjc.com:8080【主机】
    	- /api/books【路径】
    	- page=0&offset=8【参数】
    	- main【hash】
    

协议

  • 表示客户端和服务器之间使用什么语言沟通,就比如间谍和上线交流时的密语,传递什么信息都按照我们所规定的密语表示即可

主机

  • 主机表示客户端会在那一台计算机上寻找对应的资源,一般有两种表现方式,IP地址域名
  • IP地址:IP 地址是计算机在网络中唯一编号,类似我们的身份证号码
  • 域名:将难以记忆的IP地址数字转为了更容易记忆的英文单词,在访问这个域名的时候会自动被转为IP地址

端口

  • 表示客户端会在这台计算机上的那个应用程序中查找资源,类似于一家酒店就是主机,而在酒店中的各个房间中,存放这你需要获取的资源,来到酒店中,根据对应的门牌号找到对应的房间
  • 端口号是可选的,如果不选择,在 http 协议默认为 80,https 协议下默认端口号为 443

路径

  • 而在一个房间中,往往可能不止存放这一样资源,可能有许多的资源,那么这时候你可能就需要一个路径指引你,比如告诉你在某个柜子下的第几层第几个抽屉里面
  • 路径也是可以选择的,如果不填写默认为 /

参数

  • 找到对应位置之后,你又会面临一项选择,是全部拿走呢还是拿走一部分,还是拿走指定的一个呢?这个参数就是决定这件事的

hash

  • 在网络的通信中,hash 并不会起到什么作用,常见于描点链接

HTTP

  1. 前面我们提到过,协议就是一种规则,在规则内进行通信,而 http 就是一种协议,这里可以提一点,https 对比 http 是增加了安全性,其余的地方和 http 别无二致
  2. http 是基于 请求 - 响应 的方式完成通信,每一次通信都是由客户端向服务器发出请求,然后服务器处理请求并响应给客户端请求的结果
  3. 每次 请求-响应,也就是一次完整的通信都是独立的,相互之间互不干扰,因此我们也称之为无状态协议,这种情况我们就可以举例说明一下,比如,你现在需要进去一个地方,但是要进去这个地方你每次都需要获取一张令牌,且一次只能携带一张,那么第一次你拿着令牌进去了,令牌收走后,你出来之后发现你掉了东西在里面,你就和守卫说,我刚刚进去的,我掉了东西在里面,我需要回去拿,那不好意思,只认令牌不认人,你在想进来就必须再一次获取令牌才能进去,这样的通信方式即为无状态,每一次通信都互不干扰
  4. 当然这种方式肯定是存在了弊端,后面我们在拿说一下具体是什么弊端,http 又对他做了什么解决的方案
  5. 每次通信传递的消息都是纯文本信息(字符串),而文本格式必须参照 http 协议的规范

请求消息格式

  • 请求的消息格式大体可以分为三部分:
    1. 请求行:表名本次的请求的来意
    2. 请求头:描述了请求的额外信息
    3. 请求体:包含本次请求携带的数据(请求体是可以省略的,因为有时候请求并需要携带额外的参数,当然这个并不包含路径?后的参数)
请求行
  1. 是整个 http 报文的第一行字符串,它包含了三个部分:协议和协议的版本 + 请求方法(GET、POST、DELETE…) + 路径(/api/info) + 参数

  2. 在这里我们需要来仔细说一下 请求方法,首先说 GET 请求,相信大家都不陌生吧,一般我们获取数据就会使用 GET 方法,提交数据就会使用 POST 方法,修改数据就是 PATCH 方法,删除数据就是 DELETE 方法

  3. 但是在 http 协议中,其实并没有规定每个请求方式必须应该怎么做,也就表示你可以用 GET 方法去提交数据,也可以使用 POST 方法获取数据,都是可以的

  4. 只不过在实际开发中,我们有一些约定俗成的规范:

    1. GET(获取数据) POST(提交数据) DELETE(删除数据) PATCH(修改数据)

    2. GET 和 DELETE 方法不会携带请求体,而 POST 和 PATCH 方法可以有请求体

    3. 也正是因为这些约定俗成的规范,从而导致了 GET 和 POST 方法的一系列的区别:

      1. GET 方法没有请求体,所以会把参数卸载 URL 地址的参数中,这种携带参数的区别
      2. 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制
      3. GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息
      4. GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留
      5. GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有
      6. GET 在浏览器回退时是无害的,而 POST 会再次提交请求
      7. 等等…
请求头
  1. 请求头就是一系列的键值对,里面包含很多与本次业务无关的信息,浏览器在每次请求服务器时都会携带很多无关的信息,这个我们不用关心。我们只需要关心其中几个请求头即可:

  2. Host: url 地址中的主机(IP地址)

  3. User-Agent:客户端的信息描述(比如是win10,或者mac,或者安卓等...)

  4. Content-type:本次请求体的是什么格式的消息,当然如果没有请求体这个字段就没有什么意义,一般它会有常见的几种值

    1. application/x-www-form-urlencoded(早期比较流行,现在已经使用的很少了)

      表示请求体的数据格式和 URL 地址中的参数格式一样,如:name=zs&age=18,生Form表单,如果不设置 enctype 属性,默认为 application/x-www-form-urlencoded 方式提交数据。比如我们给一个 input 的 name 属性设置为 username,输入值 zhangsan ,最后请求提交时 username 就是键,zhangsan 就是值

    2. application/json(现在比较常见的传递文本数据的方式)

      表示请求体的格式是 JSON 格式,如:{ name: “zs”, age: 18 }

    3. multipart/form-data(现在比较常见的文件上传消息格式)

      一般表示文件上传,当然也是可以携带文本信息的,不过一般用于文件上传格式

    4. binary

      顾名思义,是一种二进制的数据格式

    5. application/octet-stream

      二进制数据流格式,告知浏览器这是一个字节流,浏览器处理字节流的默认方式就是下载

请求体
  1. 包含本次请求提交的字符串信息,理论上,这个请求体是可以任意形式的字符串,不过一般情况下,我们都是使用上述提前的前三种类型的字符串,也正是由于请求体的格式的类型如此之多,服务器可能无法正确识别本次消息传递的消息格式,因此我们请求时往往会附带一个 Conten-type 属性,表示本次请求体的消息格式是什么

  2. 我们可以来看一下图解实例,如图:
    在这里插入图片描述

响应消息格式

  • 响应的消息与请求的相信格式相差不多,也分为三部分组成
响应行
  1. 包含两个部分:

  2. 协议版本:表示服务器和客户端之间通信的协议版本

  3. 状态码、状态消息:表示服务器对当前请求的一个结果状态,是失败还是成功还是未找到等等

  4. 这里的话我们提一下状态码,详细的状态码代表的意思大家可以在 KOA 文档中找到:https://github.com/guo-yu/koa-guide

  5. 一般分为以下五类:

状态码分类分类描述
1**服务器收到信息,需要客户端继续执行
2**成功,操作被成功的处理
3**重定向
4**客户端错误,常见的有 404
5**服务器错误
  1. 200:请求一切正常

  2. 301:你此次请求的资源地址不存在了,并且一般会把新地址放入请求头中的 Location 中,资源被搬迁到另外一个地址了,也就是迁户口

  3. 302:你此次请求的资源临时不在了,并且一般会把新地址放入请求头中的 Location 中,以后可能还会回来的,即短暂的离家

  4. 304: 文档内容没有变化,表示你上次请求的结果与本次是一样的,不在返回结果了

  5. 400:当前请求无法被服务器处理,就比如一个外国人找上门来,但是和你噼里啪啦的说了一大堆,完全听不懂

  6. 403:服务器拒绝执行本次请求,流浪汉向你发出了救济请求,你给了一个白眼走了

  7. 404:资源不存在了,也就是人家寻亲寻到了你家,但是他的亲戚也是之前这个地方的主人已经搬走了,不在了

  8. 500:服务器内部错误,事不关己高高挂起,此时你应该去找后端打嘴炮

  9. Tips:不过在浏览器中,302 304 一般会自动帮助我们跳转到新地址

  10. 当然现在也有一种常见的做法就是,无论什么都返回 200,错误放在响应体当中

响应头
  1. 和请求头一样,响应头也是多个键值对组成的,具体由那些,取决于服务器的设置,当然最值得我们注意的依然是 Content-type,它的类型表示了响应体的数据类型
  2. 在 B/S 模式中,浏览器会自动根据 Content-type 的值来决定如何处理响应体
  3. text/plain:普通的纯文本
  4. text/html:html 文档,浏览器会自动当做页面渲染
  5. text/javascript:JavaScript 文件,浏览器会自动使用 JS 引擎解析
  6. text/css:css 文件
  7. image/jpeg:表示是一个图片,图片类型是 jpeg,也可以是 png、jpg等等
  8. attachment:浏览器发现此类型会自动触发下载功能
  9. 等等…
响应体
  • 携带了本次响应的响应结果,通常我们获取的数据就是在这里面

AJAX

什么是 AJAX

  1. AJAX是指在 web 程序中异步向服务器发送请求
  2. AJAX 是浏览器提供给 JavaScript 的一套 api ,JavaScript 就可以通过这些 api 来发送网络请求
  3. 大家也注意到了是吧,我说的是浏览器提供给 JavaScript,也就是 JavaScript 本身不具备发送网络请求通信的能力,是调用浏览器提供的 api 才可以完成的
  4. 这开发的 api 也拥有一个专有的技术名词,即 AJAX
  5. 在实现 AJAX 之初,我们使用的是 XHR(XMLHttpRequest),XMLHttpRequest 是一个构造函数,不过随着时间的推移,大家发现了 xhr 的一些缺陷,于是诞生了另外一个构造函数 fetch
  6. 但是不管是 xhr 还是 fetch 都只是实现 AJAX 的方法。代表的并不是 AJAX 本身

xhr

  1. xhr 本质只是一套 api 的使用方法,本身就不复杂,所以大家也不要对这个东西有恐惧感,我们就简单的使用一下 xhr

    // 1、创建 xhr 实例对象
    const xhr = new XMLHttpRequest()
    
    // 2、当请求状态发生改变时会运行的函数
    xhr.onreadystatechange = function () {
    	console.log('触发~')
    }
    
    // 3、配置请求
    xhr.open('GET', 'http://localhost:5000/api/moment/infos?offset=0&size=5')
    
    // 4、设置请求头-在这里我们是 get 请求,无需配置
    // xhr.setRequestHeader('Content-type', 'application/json')
    
    // 5、配置请求体,发送到服务器,如果没有请求体,传递一个 null 即可
    xhr.send(null)
    
  2. 在上面可与看到,使用非常简单,我们看一下请求是否成功呢?如图:
    在这里插入图片描述

  3. 除了第二步,其余大家可能都好理解,就是字面意思的 api,第二步是什么意思呢?这个请求字体发生改变指的有啥什么呢

    1. 1:open 方法被调用
    2. 2:send 方法被调用
    3. 3:正在接收服务器的响应消息体
    4. 4:服务器的所有内容均已接收完毕
  4. 现在问题又来了,那我怎么知道他因为什么被调用或触发的呢?上面的 1 2 3 4 数字就表明了它是在什么阶段触发的,怎么获得这个数字呢?在 xhr 中有一个 api 是 xhr.readyState,通过这个我们可以知道数字状态,如图:
    在这里插入图片描述

  5. 不过这些我们一般不是很关心,它什么时候调用对我来说不重要,重要的是不是我需要获取响应的结果啊,在上面我们通过浏览器的预览看到了成功返回的结果,如果想在代码中获取它,我们需要一个 api,xhr.responseText,代码如下:

    xhr.onreadystatechange = function () {
    	// 获取状态
    	// console.log(xhr.readyState)
    
    	// 获取响应的响应体文本
    	if (xhr.readyState === 4) {
    		console.log(xhr.responseText)
            // 不过字符串一般不利于我们查看,所以会转为 json
    		console.log(JSON.parse(xhr.responseText))
    	}
    }
    
  6. 如果你需要获取某个响应头,比如获取 Content-Type ,可以利用 xhr.getResponseHeader('Content-Type'),如下:

    xhr.onreadystatechange = function () {
    	if (xhr.readyState === 4) {
            // 获取某个响应头信息
    		console.log(xhr.getResponseHeader('Content-Type'))
    	}
    }
    
  7. api 的基本介绍就到这里了,xhr 只是属于一个 api 的调用,并不涉及很多复杂的东西,还有更多的使用方式大家自行查阅文档即可

fetch

  1. 在这里我们也简单演练一下 fetch,fetch 是返回一个 promise 对象,也就意味着我们可直接使用 async 和 await 来进行优化

  2. fetch 也比 xhr 会简单很多,为什么这么说呢,上面我们发送一个请求看起来是不是很繁琐,我们使用 fetch 来改造一下看看,如下:

    fetch('http://localhost:5000/api/moment/infos?offset=0&size=5')
    
  3. 我们看一下结果,如图:
    在这里插入图片描述

  4. 怎么样,这个代码的对比量是不是一目了然啊,不过也正常,毕竟后浪推前浪嘛,fetch 默认是 get 请求,所以如果是 get 请求可以不需要填写

  5. 那如何获取返回的响应结果呢?前面我们提到,它返回的是一个 promise,那么就可以通过 then 方法来获取,如下:

    fetch('http://localhost:5000/api/moment/infos?offset=0&size=5').then(res => {
    	console.log(res)
    })
    
  6. 那是不是真都可以获取到我们需要的响应体呢?如图:
    在这里插入图片描述

  7. 好像并没有发现响应体啊,这是为什么呢?因为当服务器收到请求之后,返回一个响应头,当收到这个响应头的时候,promise 就完成了,此时调用 then 方法获取的是一个响应对象,并不包含响应体,如果我们需要获取响应体,可以使用 jsontext两个 api,从字面意思就不难看出,两者的区别无非就是格式上的区别,一个 json 格式,一个纯文本格式

  8. 在这里,通过json 获取响应体时,它返回的也是一个 promise 对象,因此可以再次使用 then 方法获取最终的数据,如下:

    fetch('http://localhost:5000/api/moment/infos?offset=0&size=5').then(res => {
    	res.json().then(res => {
    		console.log(res)
    	})
    })
    
  9. 输出结果如下:
    在这里插入图片描述

  10. json 和 text 使用没有区别,返回结果格式不同而已

  11. 了解了基本的使用之后,那我们在来看一下 fetch 比较完整的写法,如下:

    const user = {
    	username: 'coder',
    	password: '123456'
    }
    const url = 'http://127.0.0.1:5000/api/users/register'
    
    fetch(url, {
    	method: 'POST', // 请求方法
    	body: JSON.stringify(user), // 请求体
    	headers: {
    		'Content-Type': 'application/json' // 请求头
    	}
    })
    	.then(res => res.json())
    	.then(res => {
    		console.log(res)
    	})
    
  12. 来看一下响应结果,如图:
    在这里插入图片描述

结语

相信看完前面的介绍,在看后面 xhr 和 fetch 你都会对这些 api 有着更清晰的认知,不过伴随通信的出现,往往会带来另外一个问题,那就是跨域。关于跨域,有兴趣的可以查看我的另一篇博客:跨域及其解决方案-CORS-JSONP-代理

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值