网络请求
(一)、网络请求
Section titled “(一)、网络请求”一、前后端分离
Section titled “一、前后端分离”早期的网页都是通过后端渲染来完成的:服务器端渲染(SSR,server side render):
优点:
-
提高开发效率
前后端各负其责, 前端和后端都做自己擅长的事情,不互相依赖,开发效率更快,而且分工比较均衡,会大大提高开发效率
-
用户访问速度快,提升页面性能,优化用户体验
没有页面之间的跳转,资源都在同一个页面里面,无刷新加载数据,页面片段间的切换快,使用户体验上升了一大截;前后端不分离,稍不留神会触发浏览器的重排和重绘,加载速度慢,降低用户的体验
-
增强代码可维护性,降低维护成本,改善代码的质量
前后端不分离,代码较为繁杂,维护起来难度大,成本高
-
减轻了后端服务器的请求压力
公共资源只需要加载一次,减少了HTTP请求数
-
同一套后端程序代码,不用修改就可以用于Web界面、手机、平板等多种客户端
缺点:
-
首屏渲染的时间长
常见的单页面应用,如果不对路由进行处理,在加载首页的时候,就会将所有组件全部加载,并向服务器请求数据,这必将拖慢加载速度;
-
将多个页面的资源打包糅合到一个页面,这个页面一开始需要加载的东西会非常多,而网速是一定的,所以会导致首屏渲染时间很长,
一般都会使用路由懒加载,当新跳转页面得时候才会进行加载响应得js
-
首屏渲染后,就是无刷新更新,用户体验相对较好
-
-
不利于搜索引擎的优化(SEO)
现有的搜索引擎都是通过爬虫工具来爬取各个网站的信息,这些爬虫工具一般只能爬取页面上(HTML)的内容,而前后端分离,前端的数据基本上都是存放在行为逻辑(JavaScript)文件中,爬虫工具无法爬取,无法分析出你网站到底有什么内容,无法与用户输入的关键词做关联,最终排名就低
-
不能使用浏览器里面的前进后退功能
-
一些版本较低的浏览器对其支持度不足
二、后端渲染
Section titled “二、后端渲染”服务端渲染:在服务器直接生产渲染好的对应的HTML页面,返回给客户端进行展示
当请求一个URL,URL会发送到服务器, 服务器会对该URL进行匹配,并进行一系列的处理,服务器渲染好整个页面生成一个最终的HTML页面,这个HTML页面不需要单独加载任何的js和css,可以直接交给浏览器展示。
后端路由:后端处理URL和页面之间的映射关系,当我们页面中需要请求不同的路径内容时, 交给服务器来进行处理, 服务器渲染好整个页面, 并且将页面返回给客户顿.
优点:
- 有利于SEO优化
因为后端渲染爬虫发送请求的时候,接口直接回返回一个html的页面
- 响应快,前端耗时少
前端耗时少,因为后端拼接完了HTML,不需要先下载一堆JS和CSS 后才能看到页面,即减少了首屏时间,浏览器只需要直接渲染出来
- 缓存
后端生成静态化文件。即生成缓存片段,这样就可以减少数据库查询浪费的时间了,且对于数据变化不大的页面非常高效
缺点:
一种情况是整个页面的模块由后端人员来编写和维护的. 另一种情况是前端开发人员如果要开发页面, 需要通过PHP和Java等语言来编写页面代码. 而且通常情况下HTML代码和数据以及对应的逻辑会混在一起, 编写和维护都是非常糟糕的事情
三、HTTP
Section titled “三、HTTP”**超文本传输协议(英语:HyperText Transfer Protocol,缩写:HTTP)**是一种用于分布式、协作式和超媒体信息系统的应用层协议;
- HTTP是万维网的数据通信的基础,设计HTTP最初的目的是为了提供一种发布和接收HTML页面的方法;
- 通过HTTP或者HTTPS协议请求的资源由统一资源标识符(Uniform Resource Identifiers,URI)来标识;
- HTTP是一个客户端(用户)和服务端(网站)之间请求和响应的标准。
- 通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80);
- 我们称这个客户端为用户代理程序(user agent);
- 响应的服务器上存储着一些资源,比如HTML文件和图像
- 我们称这个响应服务器为源服务器(origin server)
- 通过使用网页浏览器、网络爬虫或者其它的工具,客户端发起一个HTTP请求到服务器上指定端口(默认端口为80);
网页中资源的获取
Section titled “网页中资源的获取”我们网页中的资源通常是被放在Web资源服务器中,由浏览器自动发送HTTP请求来获取、解析、展示的。
- 目前我们页面中很多数据是动态展示的:
- 比如页面中的数据展示、搜索数据、表单验证等等,也是通过在JavaScript中发送HTTP请求获取的;
四、HTTP的组成
Section titled “四、HTTP的组成”一次HTTP请求主要包括:请求(Request)和响应(Response)
HTTP特点
Section titled “HTTP特点”1. 无连接
Section titled “1. 无连接”无连接的含义http1.0之前是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
随着时间的推移,网页变得越来越复杂,里面可能嵌入了很多图片,这时候每次访问图片都需要建立一次 TCP 连接就显得很低效
- http1.1 版本新增了 keep-alive采用了持久链接,多个请求可以共用一个TCP链接,不同的web服务器会有不同的keep-alive 的时间,node默认5s 指定的时间内保持链接,http1.0如果想要保持链接的话只要在,请求头和响应头中添加
connection:keep-alive
2. 无状态
Section titled “2. 无状态”无状态是指协议对于事务处理没有记忆能力,服务器不知道客户端是什么状态。即我们给服务器发送 HTTP 请求之后,服务器根据请求,会给我们发送数据过来,但是,发送完,不会记录任何信息。
- HTTP 是一个无状态协议,这意味着每个请求都是独立的
灵活:HTTP允许传输任意类型的数据对象。正在传输的类型由Content-Type(Content-Type是HTTP包中用来表示内容类型的标识)加以标记。
支持客户/服务器模式。
4. 简单快速
Section titled “4. 简单快速”简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、HEAD、POST。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快。
1)请求 request
Section titled “1)请求 request”HTTP 的请求报文分为三个部分 请求行、请求头和 (请求空行 请求体),
1、请求行Request Line
Section titled “1、请求行Request Line”请求行(Request Line)分为三个部分:请求方法、请求地址和协议及版本,以CRLF(rn)结束。
1. Request Methods
Section titled “1. Request Methods”RFC (Request For Comments)-意即“请求评议”。定义了一组请求方式,来表示要对给定资源执行的操作:
HTTP/1.1 定义的请求方法有8种:GET、POST、PUT、DELETE、PATCH、HEAD、OPTIONS、TRACE,
- 最常的两种GET和POST,如果是RESTful接口的话一般会用到GET、POST、DELETE、PUT。
-
GET:GET 方法请求一个指定资源的表示形式,使用 GET 的请求应该只被用于获取数据。
-
HEAD:HEAD 方法请求一个与 GET 请求的响应相同的响应,但没有响应体。
比如在准备下载一个文件前,先获取文件的大小,再决定是否进行下载;
-
POST:POST 方法用于将实体提交到指定的资源。
-
PUT:PUT 方法用请求有效载荷(payload)替换目标资源的所有当前表示;
印象中PUT的默认 contype-type: application/x-www-form-urlencoded 方式提交数据。
-
DELETE:DELETE 方法删除指定的资源;
-
PATCH:PATCH 方法用于对资源应部分修改;
-
CONNECT:CONNECT 方法建立一个到目标资源标识的服务器的隧道,通常用在代理服务器,网页开发很少用到。
-
TRACE:TRACE 方法沿着到目标资源的路径执行一个消息环回测试。
- 注意,仅有POST、PUT以及PATCH这三个动词时会包含请求体,而
GET、HEAD、DELETE、CONNECT、TRACE、OPTIONS这几个动词时不包含请求体。
1. url 地址
Section titled “1. url 地址”3. 协议版本
Section titled “3. 协议版本”- HTTP/0.9
- 发布于1991年;
- 只支持GET请求方法获取文本数据,当时主要是为了获取HTML页面内容;
- HTTP/1.0
- 发布于1996年;
- 支持POST、HEAD等请求方法,支持请求头、响应头等,支持更多种数据类型(不再局限于文本数据) ;
- 但是浏览器的每次请求都需要与服务器建立一个TCP连接,请求处理完成后立即断开TCP连接,每次建立连接增加了性能损耗;
- HTTP/1.1(目前使用最广泛的版本)
- 发布于1997年;
- 增加了PUT、DELETE等请求方法;
- 采用持久连接(Connection: keep-alive),多个请求可以共用同一个TCP连接;
- 2015年,HTTP/2.0
- 2018年,HTTP/3.0
2、请求头 Request Header
Section titled “2、请求头 Request Header”在request对象的header中也包含很多有用的信息,客户端会默认传递过来一些信息:
1. content-type是这次请求携带的数据的类型
Section titled “1. content-type是这次请求携带的数据的类型”主要功能就是给后台返回的是一个什么样类型的数据,是文本类型还是json类型
get请求,设置就有不设置就没有 默认在 FromData里面,
post请求,如果有请求数据传入后台,浏览器默认加入一个
Content-Type: text/plain;charset=UTF-8
-
application/x-www-form-urlencoded:表示数据被编码成以 ’&’ 分隔的键 - 值对,同时以 ’=’ 分隔键和值
-
application/json:表示是一个json类型;
请求头中的 Content-type 是告诉服务器返回的数据类型
-
text/plain:表示是文本类型;
-
application/xml:表示是xml类型;
-
multipart/form-data:表示是上传文件;
FormData 对象,以
multipart/form-data形式发送数据
2. Accept(接受) 指定客户端可以接收的内容类型
Section titled “2. Accept(接受) 指定客户端可以接收的内容类型”- Accept: text/plain,
- text/html,
- application/json
3.Connection: keep-alive
Section titled “3.Connection: keep-alive”http是基于TCP协议的,但是通常在进行一次请求和响应结束后会立刻中断;
-
在http1.0中,如果想要继续保持连接:
- 浏览器需要在请求头中添加 connection: keep-alive;
- 服务器需要在响应头中添加 connection:keey-alive;
- 当客户端再次放请求时,就会使用同一个连接,直接一方中断连接;
-
在http1.1中,所有连接默认是 connection: keep-alive的
- 不同的Web服务器会有不同的保持 keep-alive的时间;
- Node中默认是5s中;
4. Accept-Charset 浏览器可以接受的字符编码集。
Section titled “4. Accept-Charset 浏览器可以接受的字符编码集。”Accept-Charset: iso-8859-5
5. Accept-Encoding
Section titled “5. Accept-Encoding”指定浏览器可以支持的web服务器返回内容压缩编码类型。 Accept-Encoding: compress, gzip
6. Accept-Language 浏览器可接受的语言
Section titled “6. Accept-Language 浏览器可接受的语言”Accept-Language: en,zh
7. Authorization HTTP授权的授权证书
Section titled “7. Authorization HTTP授权的授权证书”Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==
8. User-Agent User-Agent的内容包含发出请求的用户信息
Section titled “8. User-Agent User-Agent的内容包含发出请求的用户信息”User-Agent: Mozilla/5.0 (Linux; X11)
9.content-length:文件的大小长度
Section titled “9.content-length:文件的大小长度”Content-Length首部指示出报文中实体主体的字节大小. 这个大小是包含了所有内容编码的, 比如, 对文本文件进行了gzip压缩的话, Content-Length首部指的就是压缩后的大小而不是原始大小.
3、请求体 Response
Section titled “3、请求体 Response”根据应用场景的不同,HTTP请求的请求体有三种不同的形式。
-
知识点:
-
Request Payload更准确的说是http request的payload body。一般用在数据通过POST请求或者PUT请求。
-
FormDate 是get 和 form表单 就是查询字符串
- 所以区别就是,他们只是因为Content-Type设置的不同,并不是数据提交方式的不同,这两种提交都会将数据放在message-body中
-
-
application/json
这个 Content-Type 作为响应头大家肯定不陌生。用来告诉服务端消息主体是序列化后的 JSON 字符串。
特别适合 RESTful 的接口。
-
text/xml
XML-RPC(XML Remote Procedure Call)
XML 作为编码方式的远程调用规范。
-
Query String:application/x-www-form-urlencoded 查询字符串
这算是最常见的 POST 提交数据的方式了。浏览器的原生
<form>表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。请求类似于下面这样(无关的请求头在本文中都省略掉了):
2)响应 response
Section titled “2)响应 response”类似于请求消息中的请求头,其格式和请求头信息格式一样,即响应头:响应头值。
- response 代表响应, 可以通过该对象分别设置Http响应: 状态行 响应头 响应体
1、HTTP响应的细节—状态行
Section titled “1、HTTP响应的细节—状态行”-
状态行格式: HTTP版本协议 状态码 状态码原因短语
-
举例:HTTP/1.1 200 OK
-
状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类,如下所示:
Http状态码非常多,可以根据不同的情况,给客户端返回不同的状态码;
- 表示请求成功,要求客户端继续提交下一次请求才能完成整个处理过程(
100–199) - 成功响应 (
200–299) - 重定向消息 为完成请求,客户需进一步细化请求。例如,请求的资源已经移动一个新地址,常用302、307和304(
300–399) - 客户端错误响应 (
400–499) - 服务端错误响应 (
500–599)
- 常见HTTP状态码 状态描述 信息说明
| Header | 解释 |
|---|---|
| 200 | OK 客户端请求成功 |
| 201 | Created POST请求,创建新的资源 |
| 301 | Moved Permanently 请求资源的URL已经修改,响应中会给出新的URL |
| 400 | Bad Request 客户端的错误,服务器无法或者不进行处理 |
| 401 | Unauthorized 未授权的错误,必须携带请求的身份信息 |
| 403 | Forbidden 客户端没有权限访问,被拒接 |
| 404 | Not Found 服务器找不到请求的资源。 |
| 500 | Internal Server Error 服务器遇到了不知道如何处理的情况。 |
| 503 | Service Unavailable 服务器不可用,可能处理维护或者重载状态,暂时无法访问 |
响应的header中包括一些服务器给客户端的信息
-
用于在响应消息中向客户端传递附加信息,包括服务程序的名称,要求客户端进行认证的方式,请求的资源已移动到新地址等.
主要: Location , Server , WWW-Authenticate(认证头)
-
Connection:处理完这次请求后,是断开连接还是继续保持连接;
-
Content-Encoding:服务器通过这个头,告诉游览器数据的压缩格式;
-
Content-Length:服务器通过这个头,告诉游览器回送数据的长度;
-
Content-Type:服务器通过这个头,告诉游览器回送数据的类型;
这里的 content-Type:的作用就是服务器告诉浏览器返回的数据格式,是服务器来设置的
浏览器可能会对不同的格式来进行处理
问题:设置了content-type之后浏览器会根据不同类型来显示到页面上例如text/html,后端做法,但是ajax 则不需要,正文过来什么就是什么, 通过ajax 或者JSON 进行转换json对象,至于 text/html 和 application/json 的区别是什么后面在说
- 三种组成部分
media type:声明传输数据的媒体类型( MIME );charset:声明传输数据采用的是何种字符集;boundary:数据分界符,有多部分数据实体时(multipart/form-data、multipart/byteranges),该指令是必需的,用于封装消息的多个部分的边界;其由 1 到 70 个字符组成,浏览器中会自动生成,该字符集对于通过网关鲁棒性良好,不以空白结尾。
-
Date:当前时间值;
-
Server:服务器通过这个头,告诉游览器服务器的类型;
7.Vary:Accept-Encoding----明确告知缓存服务器Accept-Encoding字段的内容,分别缓存不同的版本,参考:https://imququ.com/post/vary-header-in-http.html
8.X-Powered-By:服务器告知客户机网站是用何种语言或框架编写的。
即响应正文
- 响应头之后紧跟着一个空行,然后接响应体。响应体就是Web服务器发送到客户端的实际内容。除网页外,响应体还可以是诸如Word、Excel或PDF等其他类型的文档,具体是哪种文档类型由Content-Type指定的MIME类型决定。
(二)、AJAX发送请求
Section titled “(二)、AJAX发送请求”JAX 是异步的 JavaScript 和 XML(Asynchronous JavaScript And XML)
- 它可以使用 JSON,XML,HTML 和 text 文本等格式发送和接收数据;
AJAX的请求过程
Section titled “AJAX的请求过程”-
第一步:创建网络请求的AJAX对象(使用XMLHttpRequest)
-
第二步:监听XMLHttpRequest对象状态的变化,或者监听onload事件(请求完成时触发);
-
第三步:配置网络请求(通过open方法);**默认异步请求 设置同步请求第三个参数为:false **
-
第四步:发送send网络请求
const url = "http://123.207.32.32:8000/home/multidata"//1、创建XMLHttpRequest对象const xhr = new XMLHttpRequest()//2、监听状态等待响应xhr.onreadystatechange = function() {//监听XMLHttpRequest对象状态的变化if(xhr.readyState !== XMLHttpRequest.DONE) returnconsole.log(xhr.readyState)}//3、method, url, 是否为异步async//同步 truexhr.open("get",url, true)//4、发送请求xhr.send()
1、XMLHttpRequest的state(状态)
Section titled “1、XMLHttpRequest的state(状态)”在一次网络请求中看到状态发生了很多次变化,这是因为对于一次请求来说包括如下的状态:
- Api: xhr.onreadystatechange = function() {} 回调函数
- 注意:这里是记录的XMLHttpRequest对象的状态变化
- 准备状态 readyState
| 值 | 状态 | 描述 |
|---|---|---|
| 0 | UNSENT | 代理被创建,但尚未调用 open() 方法。 |
| 1 | OPENED | open() 方法已经被调用。 |
| 2 | HEADERS_RECEIVED | send() 方法已经被调用,并且头部和状态已经可获得。 |
| 3 | LOADING 下载中; | responseText 属性已经包含部分数据。 |
| 4 | DONE | 下载操作已完成。 |
2、XMLHttpRequest其他事件监听
Section titled “2、XMLHttpRequest其他事件监听”除了onreadystatechange还有其他的事件可以监听
-
loadstart:请求开始。
-
progress: 一个响应数据包到达,此时整个 response body 都在 response 中。
-
abort:调用 xhr.abort() 取消了请求。
-
error:发生连接错误,例如,域错误。不会发生诸如 404 这类的 HTTP 错误。
-
load:请求成功完成。就是XMLHttpRequest对象的 done状态
可以使用load事件来监听请求是否成功
const url = "http://123.207.32.32:8000/home/multidata"//1、创建XMLHttpRequest对象const xhr = new XMLHttpRequest()//method, url, 是否为异步async truexhr.open("get",url, true)//发送请求xhr.send()//监听状态xhr.onload = function() {//准备状态console.log(xhr.readyState)} -
timeout:由于请求超时而取消了该请求(仅发生在设置了 timeout 的情况下)。
-
loadend:在 load,error,timeout 或 abort 之后触发 加载结束
3、设置请求头
Section titled “3、设置请求头”- 注意:设置请求头要放在 open 配置请求信息的下面
//XMLHttpRequest 的请求方式 const xhr = new XMLHttpRequest() xhr.responseType= "json" xhr.open("post", "http://123.207.32.32:1888/02_param/posturl") //放在配置请求信息的后面 xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded") xhr.send("name=why&age=18&address=广州市") xhr.onload = function() { console.log(xhr.response) }响应数据和响应类型
Section titled “响应数据和响应类型”XMLHttpRequest 实例对象中的 response 属性返回响应的正文内容
-
注意:返回的类型取决于
responseType的属性设置;text作为默认值。- 可以设置为 json
-
其他属性:
responseText、responseXML-
早期通常服务器返回的数据是普通的文本和XML,所以我们通常会通过responseText、 responseXML来获取响应结果;
起始就是方便不用写
responseType个人觉得 -
之后将它们转化成JavaScript对象形式;
-
目前服务器基本返回的都是json数据,直接设置为json即可;
-
-
返回json对象的方式
在配置请求信息钱配置 responseType,否则默认是文本
// 告知xhr获取到的数据的类型xhr.responseType = "json"
1. HTTP获取响应的状态status
Section titled “1. HTTP获取响应的状态status”获取响应行的 响应码和 状态码,原因短语
- 获取响应码:
xhr.status - 状态码原因短语:
xhr.statusText
2、HTTP常见响应行状态码、状态码原因短语
Section titled “2、HTTP常见响应行状态码、状态码原因短语”| 常见HTTP状态码 | 状态描述:原因短语 | 信息说明 |
|---|---|---|
| 200 | OK | 客户端请求成功 |
| 201 | Created POST | 请求,创建新的资源 |
| 301 | Moved Permanently | 请求资源的URL已经修改,响应中会给出新的URL |
| 400 | Bad Request | 客户端的错误,服务器无法或者不进行处理 |
| 401 | Unauthorized | 未授权的错误,必须携带请求的身份信息 |
| 403 | Forbidden | 客户端没有权限访问,被拒接 |
| 404 | Not Found | 服务器找不到请求的资源。 |
| 500 | Internal Server Error | 服务器遇到了不知道如何处理的情况。 |
| 503 | Service Unavailable | 服务器不可用,可能处理维护或者重载状态,暂时无法访问 |
XMLHttpRequest请求方式
Section titled “XMLHttpRequest请求方式”-
常见的传递给服务器数据的方式有如下几种:
-
注意:
- 只有get 查询字符串的方式和 FormData 的实例对象的方式不用设置请求头
- 只有post请求有请求体
方式一:GET请求的 query 参数
Section titled “方式一:GET请求的 query 参数”- get 也能发送
FormData 对象,会被转成query的形式
//XMLHttpRequest 的请求方式 const xhr = new XMLHttpRequest() xhr.responseType= "json" //get 请求和json字符串, 设置响应类型 xhr.open("get", "http://123.207.32.32:1888/02_param/get?name=why&age=18&address=广州市") xhr.send() xhr.onload = function() { console.log(xhr.response) }方式二:POST请求 x-www-form-urlencoded 格式
Section titled “方式二:POST请求 x-www-form-urlencoded 格式” xhr.open("post", "http://123.207.32.32:1888/02_param/posturl") xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded") xhr.send("name=why&age=18&address=广州市")方式三:POST请求 FormData 格式
Section titled “方式三:POST请求 FormData 格式”-
知识点:FormData 对象,以
multipart/form-data形式发送数据本接口和此方法都相当简单直接。如果送出时的编码类型被设为 “
multipart/form-data”,它会使用和表单一样的格式。 -
注意:这里的 FormData 的实例对象要和发送请求在统一时机内执行,否则获取不到form表单的value。
在同一个代码块内执行,避免获取不到value
//form表单元素 const formEl = document.querySelector("form") const btnEl = document.querySelector(".btn")
btnEl.addEventListener("click", function() { //这里的 FormData 的实例对象要和发送请求**在统一时机内执行**,否则获取不到form表单的value const formdata = new FormData(formEl) xhr.open("post", "http://123.207.32.32:1888/02_param/postform") xhr.send(formdata) })方式四:POST请求 JSON 格式
Section titled “方式四:POST请求 JSON 格式”-
注意事项:记得添加请求头
要将json对象转成json字符串
btnEl.addEventListener("click", function() { xhr.open("post", "http://123.207.32.32:1888/02_param/postjson") xhr.setRequestHeader("content-type", "application/json") xhr.send(JSON.stringify(obj)) })-
知识点
-
一个请求伴随着header设置为Content-Type: application/json method 为POST 的时候,请求信息会放在 请求空行里
POST /some-path HTTP/1.1Content-Type: application/json{ "foo" : "bar", "name" : "John" } -
method="post",并且设置了Content-Type: application/x-www-form-urlencoded或者Content-Type: multipart/form-data。也会放在请求空行里POST /some-path HTTP/1.1Content-Type: application/x-www-form-urlencodedfoo=bar&name=John
- 以上两种 都是放在 request的payload里
-
传统的Form表单提交,Content-Type默认为
application/x-www-form-urlencoded会放在请求头的
FormData当中 -
get 请求 Content-Type: application/x-www-form-urlencoded 的时候也是在 FormData 中
-
延迟时间timeout 和 取消请求
Section titled “延迟时间timeout 和 取消请求”网络请求的过程中,为了避免过长的时间服务器无法返回数据,通常我们会为请求设置一个超时时间:timeout。
- 当达到超时时间后依然没有获取到数据,那么这个请求会自动被取消掉;
- 默认值为0,表示会一直等待服务器的响应
- 我们也可以通过abort方法强制取消请求
- 注意:同样在open 配置请求信息前设置
const timeUrl = "http://123.207.32.32:1888/01_basic/timeout"; const xhr = new XMLHttpRequest(); xhr.responseType = "json"; //配置超时间 xhr.timeout = 3000;
xhr.onload = function() { console.log(xhr.response, xhr.status, xhr.statusText) } //监听请求超时 xhr.ontimeout = function() { console.log("请求超时") } xhr.open("get", timeUrl) xhr.send()
const btnEl = document.querySelector(".btn") btnEl.addEventListener("click", function() { //取消请求 xhr.abort() })XMLHttpRequest文件上传
Section titled “XMLHttpRequest文件上传”封装请求函数
Section titled “封装请求函数”function request({ url, method, data = {}, header = {}, success, failure} = {}) { const xhr = new XMLHttpRequest() xhr.responseType = 'json' xhr.onload = function() { if(xhr.status*1 >=200 && xhr.status*1 <= 300) { success(xhr.response) } else { failure(xhr.status) } }
//发送get请求 if(method.toUpperCase() == "GET") { if(Object.keys(data).length !== 0) {
const paramArr = [] for (const key in data) { if (Object.hasOwnProperty.call(data, key)) { const el = data[key]; paramArr.push(`${key} = ${el}`) } } const queryString = paramArr.join("&") xhr.open("get", url+"?"+ queryString) //设置header if(Object.keys(header).length !== 0) { for (const key in header) { if (Object.hasOwnProperty.call(header, key)) { const el = header[key]; xhr.setRequestHeader(key, el) } } } xhr.send()
} else { xhr.open("get", url) //设置header if(Object.keys(header).length !== 0) { for (const key in header) { if (Object.hasOwnProperty.call(header, key)) { const el = header[key]; xhr.setRequestHeader(key, el) } } } xhr.send() } } //发送post请求 if(method.toUpperCase() == "POST") { xhr.open("post", url) //设置header if(Object.keys(header).length !== 0) { for (const key in header) { if (Object.hasOwnProperty.call(header, key)) { const el = header[key]; xhr.setRequestHeader(key, el) } } } xhr.send(JSON.stringify(data)) }
}实现文件上传
Section titled “实现文件上传”表单的多文件上传
Section titled “表单的多文件上传”<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Document</title></head><body> <!-- 设置 multiple --> <input class="inp" type="file", name="file" multiple> <button class="btn">上传</button></body><script>const url = "http://123.207.32.32:1888/02_param/upload"const btnEl = document.querySelector(".btn")const inpEl = document.querySelector(".inp")btnEl.addEventListener("click", function() { // console.log(typeof inpEl.files) const formdata = new FormData() for(let i = 0; i < inpEl.files.length; i++) { console.log(inpEl.files[i]) //批量添加,form 中的 input.files[i]元素,这个要后台配置分段上传 //添加选中一个就是一个 formdata.append(inpEl.files[i].name, inpEl.files[i]) }
const xhr = new XMLHttpRequest();xhr.onload = function() { console.log(xhr.response)}xhr.open("post", url) //上传xhr.send(formdata)})</script></html>(三)、Fetch发送请求
Section titled “(三)、Fetch发送请求”Fetch可以看做是早期的XMLHttpRequest的替代方案,它提供了一种更加现代的处理方案:
-
特点:
-
返回值是一个Promise,提供了一种更加优雅的处理结果方式
通过resolve,reject 的方式调用
-
不像XMLHttpRequest一样,所有的操作都在一个对象上
-
Fetch的使用
Section titled “Fetch的使用”-
参数
-
参数一 input:input:定义要获取的资源地址,可以是一个URL字符串,也可以使用一个Request对象(实验性特性)类型;
就是url
-
参数二 init:配置 init 初始化对象 {} 具体参数可以查看mdn
-
init 初始化配置对象(可选)
Section titled “init 初始化配置对象(可选)”一个配置项对象,包括所有对请求的设置。可选的参数有:
-
method: 请求使用的方法,如GET、POST。 -
headers: 请求的头信息,形式为Headers的对象或包含ByteString值的对象字面量。 -
body: 请求的 body 信息:可能是一个Blob、BufferSource、FormData、URLSearchParams或者USVString对象。注意 GET 或 HEAD 方法的请求不能包含 body 信息。需要传给后台的请求信息,message body
const formData = new FormData()formData.append("name", "why")formData.append("age", 18)const response = await fetch("http://123.207.32.32:1888/02_param/postform", {method: "post",//可以是一个form表单对象body: formData//可以设置请求头,是一个对象/*headers: {'Content-Type': 'application/json'// 'Content-Type': 'application/x-www-form-urlencoded',},*///可以是json字符串// body: JSON.stringify({// name: "why",// age: 18// })})
Fetch数据的响应(Response)
Section titled “Fetch数据的响应(Response)”Fetch的数据响应主要分为两个阶段
-
阶段一:当服务器返回了响应(response)
fetch 返回的 promise
- fetch 无法建立一个 HTTP 请求,例如网络问题,亦或是请求的网址不存在,那么 promise 就会 reject
- 异常的 HTTP 状态,例如 404 或 500,不会导致出现 error
-
第二阶段,为了获取 response body,我们需要使用一个其他的方法调用
- response.text() —— 读取 response,并以文本形式返回 response;
- response.json() —— 将 response 解析为 JSON;
-
获取response 的 HTTP 状态
- tatus:HTTP 状态码,例如 200;
- ok:布尔值,如果 HTTP 状态码为 200-299,则为 true;
fetch 使用注意事项(知识点)
Section titled “fetch 使用注意事项(知识点)”-
从
fetch()返回的 Promise 不会被标记为 reject,即使响应的 HTTP 状态码是 404 或 500。就是fetch() 返回的一定是 promise.resolve()
-
如果响应的 HTTP 状态码不在 200 - 299 的范围内,则设置 resolve 返回值的
ok属性为 false返回的then 参数对象会有一个ok属性
-
获取 JSON 的内容,我们需要使用
json()方法(该方法返回一个将响应 body 解析成 JSON 的 promise) -
获取文本,使用的 text() 方法 同样返回一个将响应 body 解析成 JSON 的 promise
Fetch网络请求的演练
Section titled “Fetch网络请求的演练”Fetch基本使用
Section titled “Fetch基本使用” //fetch的基本使用 const url = "http://123.207.32.32:8000/home/multidata" fetch(url).then(data => { //返回 文本类型 console.log(data.text().then(res => console.log(res))) //返回 json 格式 // data.json().then(res => console.log(res)) }).catch(err => console.log(err))
//使用异步函数 async function foo(url) { const data = await fetch(url); const res = data.json() return res }
foo(url).then(res => { console.log(res) }).catch(err => { console.log(err) })Fetch Post 请求
Section titled “Fetch Post 请求”配置查mdn文档就好了
async function getData() { const data = await fetch(postUrl, { method:"post", headers: { "Content-Type": "application/json" }, body: JSON.stringify(obj) })
return data.json() }
getData().then(res => console.log(res))Fetch文件上传
Section titled “Fetch文件上传”- FormData 对象,以
multipart/form-data形式发送数据
<body> <input type="file" class="inp"> <button class="btn">上传</button></body><script> const url = "http://123.207.32.32:1888/02_param/upload" const btnEl = document.querySelector(".btn") const inpEl = document.querySelector(".inp")
btnEl.addEventListener("click", function() { const formdata = new FormData() formdata.append("above",inpEl.files[0]) fetch(url, { method: "post", body: formdata }).then(res => res.json()).then(res => console.log(res)) })
</script>XMLHttpRequest和Fetch请求的异同
Section titled “XMLHttpRequest和Fetch请求的异同”XMLHttpRequest
Section titled “XMLHttpRequest”- 所有的方法都在 xhr 对象里
- 可以取消发送请求,设置timeout
- 兼容性好
- 返回的是promise 基于标准的promise实现的。可以避免回调地狱
- 不会接受跨域 cookies;
- 默认fetch 不会发送 cookies
- 有些浏览器可能不支持。
- 不支持超时设置超时事件
- 没有办法检测请求进度 XHR可以
Fetch 跨域
Section titled “Fetch 跨域”如果服务器返回的 response 头包含 “Access-Control-Allow-Origin:*”或者 *为与请求源相同的地址。即服务器支持浏览器跨域访问。若不包含需求修改服务器端,与浏览器端请求。
- CORS的具体原理可以参考 http://www.ruanyifeng.com/blog/2016/04/cors.html
使用fetch 发起跨域请求时,CORS(跨域资源共享Cross-origin resource sharing)
Section titled “使用fetch 发起跨域请求时,CORS(跨域资源共享Cross-origin resource sharing)”如果服务器不支持CORS,fetch提供了三种模式,其中no-cors可以继续访问服务器
fetch的mode配置项有3个值,如下:
-
same-origin:该模式是不允许跨域的,它需要遵守同源策略,否则浏览器会返回一个error告知不能跨域;其对应的response type为basic。
-
cors: 该模式支持跨域请求,顾名思义它是以CORS的形式跨域;当然该模式也可以同域请求不需要后端额外的CORS支持;其对应的response type为cors。
-
no-cors: 该模式用于跨域请求但是服务器不带CORS响应头,也就是服务端不支持CORS;这也是fetch的特殊跨域请求方式;其对应的response type为opaque。
针对跨域请求,cors模式是常见跨域请求实现,但是fetch自带的no-cors跨域请求模式则较为陌生,该模式有一个比较明显的特点:
该模式允许浏览器发送本次跨域请求,但是不能访问响应返回的内容,这也是其response type为opaque透明的原因。
- 注意: cors 支持 三种content-type 不支持 application/json
- application/x-www-form-urlencoded
- multipart/form-data
- text/plain
为了能将请求 text/plain的body 解析为json对象,可以参考
四、请求框架设计
Section titled “四、请求框架设计”4.1、封装网络请求
Section titled “4.1、封装网络请求”4.1.1、配置文件
Section titled “4.1.1、配置文件”let BASE_URL = ''
const TIME_OUT = 10000
if (process.env.NODE_ENV === 'development') {
BASE_URL = '开发环境ip'
} else if (process.env.NODE_ENV === 'production') {
BASE_URL = '生产环境ip'
} else {
BASE_URL = '测试环境ip'
}
export { BASE_URL, TIME_OUT }4.1.2、创建请求class
Section titled “4.1.2、创建请求class”为什么要用class呢,避免网路请求框架修改,这样只用改class就可以了
import axios from 'axios'import type { AxiosInstance } from 'axios'import type { HYRequestInterceptors, HYRequestConfig } from './type'
class YRequest {
instance: AxiosInstance
interceptors?: HYRequestInterceptors
constructor(config: HYRequestConfig) {
//创建axios实例
this.instance = axios.create(config)
//保存基本信息
this.interceptors = config.interceptors
//使用拦截器
//从config钟取出的拦截器是对应的实例的拦截器
this.instance.interceptors.request.use(
this.interceptors?.requestInterceptor,
this.interceptors?.requestInterceptorCatch
)
this.instance.interceptors.response.use(
this.interceptors?.responseInterceptor,
this.interceptors?.requestInterceptorCatch
)
//所有的实例都有的拦截器
this.instance.interceptors.request.use(
(config) => {
console.log('所有的实例都有的拦截器: 请求拦截成功') return config
},
(err) => {
console.log('所有的实例都有的拦截器: 请求拦截失败')
return err
}
)
this.instance.interceptors.response.use(
(res) => {
console.log('所有的实例都有的拦截器: 响应拦截成功')
return res.data
},
(err) => {
console.log('所有的实例都有的拦截器: 响应拦截失败')
//例子:判断不同httpErrorCode显示不同错误信息
if (err.response.status === 404) {
console.log('404错误~')
}
return err
}
)
}
request<T>(config: HYRequestConfig<T>): Promise<T> {
return new Promise((resolve, reject) => {
//单个请求拦截config的处理
if (config.interceptors?.requestInterceptor) { // 拦截之后会返回新的配置,这个要自定义 config = config.interceptors.requestInterceptor(config) }
this.instance.request<any, T>(config).then((res) => {
//单个请求响应拦截
if (config.interceptors?.responseInterceptor) {
res = config.interceptors.responseInterceptor(res)
}
//将结果返回出去 resolve(res)
})
.catch((err) => {
reject(err)
return err
})
})
}
get<T>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'GET' })
}
post<T>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'POST' })
}
delete<T>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'DELETE' })
}
patch<T>(config: HYRequestConfig<T>): Promise<T> {
return this.request<T>({ ...config, method: 'PATCH' })
}
}
export default HYRequest4.1.3 ts文件
Section titled “4.1.3 ts文件”import type { AxiosRequestConfig, AxiosResponse } from 'axios'
export interface HYRequestInterceptors<T = AxiosResponse> {
requestInterceptor?: (config: AxiosRequestConfig) => AxiosRequestConfig
requestInterceptorCatch?: (error: any) => any
responseInterceptor?: (res: T) => T
responseInterceptorCatch?: (error: any) => any
}
export interface HYRequestConfig<T = AxiosResponse> extends AxiosRequestConfig {
interceptors?: HYRequestInterceptors<T>
}4.1.4 创建实例
Section titled “4.1.4 创建实例”///index.ts文件import HYRequest from './request'
import { BASE_URL, TIME_OUT } from './request/config'
const hyRequest = new HYRequest({
baseURL: BASE_URL,
timeout: TIME_OUT,
interceptors: {
requestInterceptor: (config) => {
const token = '' // 这里看情况加一个是否需要认证,其实后端直接去掉就可以,除非是涉及到前端逻辑的情况下 if (token) { config.headers!.Authorization = token } console.log('请求成功拦截') return config },
requestInterceptorCatch: (err) => {
console.log('请求失败拦截')
return err
},
responseInterceptor: (config) => {
console.log('响应成功拦截')
return config
},
responseInterceptorCatch: (err) => {
console.log('响应失败拦截')
return err }
}
})
export default hyRequest
