【二十一】请求封装

295 阅读11分钟

1.jpg

前言

本篇博客主要回顾了日常请求的封装以及拓展,便于后续开发新项目时快速使用

面试回答

1.get和post的区别:从传参上来讲,get请求会将参数拼接到url上,而post则是放在请求体中。从数据量来讲,get请求有url长度限制,所以post请求允许发送的数据比get更大。从安全性上来讲,post请求不会被缓存、保存在记录上,因此更加安全。从数据类型上来讲,post请求能发送更多的数据类型。

2.options请求:option请求一般出现在复杂请求中,比如put、delete请求或者带json格式、自定义头部这样的。复杂请求可能会对数据产生副作用,所以在请求前会先发起option请求,确认当前页面是否在许可名单中,然后再发起正常的请求。

3.本地存储:常用的本地缓存有cookie、localStorage、sessionStorage、indexedDB,cookie一般用于状态存储,它的容量较小,请求经常会带上这个cookie,造成性能浪费,且容易被获取篡改;sessionStorage属于会话级存储,容量相对较大,不参与请求,能够持久化存储,用于临时保存同一窗口的数据,在关闭窗口或页签之后将会删除这些数据。localStorage与sessionStorage基本一致,只不过localStorage关闭浏览器也不会被清理,所以需要手动清理,避免浪费;indexedDB也是持久化存储,不过用到的会比较少。

4.轮询:ajax轮询一般分为两种。一种是设定一个定时器,时间一到就发请求,这种比较消耗资源。还有一种是ajax长轮询,它需要服务器接到请求后保持连接,直到有新消息才关闭连接。实现上就是在第一次请求的时候,在成功以及失败的回调中再起发起请求,不过这种方式数据顺序无法保证,难以管理。Websocket是html5中的一个持久化协议,它最大的特点就是可以双向通信,允许服务器主动的向客户端推送信息,这种方式实时性比较强,创建连接后,可以通过websocket的数据包头部进行数据交换。使用时,一般我们会new一个websocket对象,然后用websocket对应的事件去执行,比如open建立连接、send发送数据、message接收数据、error通信异常、close关闭连接。

知识点

从本质上来讲,封装http请求,相当于写一个公共方法,然后供其他组件使用,只不过书写方式以及拓展可以拿来讲一讲。

1.GET和POST的区别

  • 使用Get请求安全性低,可以收藏为书签,可以被缓存,参数在URL中显示,且不同浏览器存在不同的长度限制;而Post请求反之,不可以被收藏,除非在响应头中设置Cache-Control/Expires字段,否则也不可以被缓存,参数储存在请求体重,无长度限制。
  • GET请求一般不具有请求体,因此只能进行url编码,只允许ASCII字符,而POST请求支持多种编码方式,也支持二进制数据
  • 使用Get请求发送数据量小,Post请求发送数据量大
  • GET请求具有幂等性(多次请求不会对资源造成影响),POST请求不幂等;
  • GET请求一般不具有请求体,请求中一般不包含100-continue 协议,所以只会发一次请求,而POST请求在发送数据到服务端之前允许双方"握手",客户端先发送Expect:100-continue消息,询问服务端是否愿意接收数据,接收到服务端正确的100-continue应答后才会将请求体发送给服务端,服务端再响应200返回数据。

2.复杂请求

符合以下条件的请求,即为简单请求:

  1. 请求方式为GET、HEAD、POST时的请求;
  2. 认为设置规范集合之内的首部字段, 如Accept/Accept-Language/Content-Language/Content-Type/DPR/Downlink/Save-Data/Viewport-Width/Width
  3. Content-Type的值仅限于下列三者之一,即application/x-www-form-urlencoded、multipart/form-data、text/plain;
  4. 请求中的任意XMLHttpRequestUpload对象均没有注册任何事件监听器
  5. 请求中没有使用ReadableStream对象。

除此之外,均为复杂请求。

复杂请求就会触发options预检请求,预检请求会额外占用服务器资源,还会延迟真正请求的发起时间,导致页面上性能变差 。

3.本地存储

  • cookie

cookie本质上是浏览器里面存储的一个很小的文本文件,内部以键值对的方式存储。向同一个域名下发送请求都会携带相同的Cookie,服务器拿到Cookie进行解析,就能拿到客户端的状态。

容量缺陷:Cookie的体积上限只有4KB,只能用来存储少量的信息。

性能缺陷:Cookie是紧跟域名的,不管域名下面的某个地址需不需要这个Cookie,它都会携带上完整的Cookie。这样随着请求数据的增多,很容易造成性能上的浪费。

安全缺陷:由于Cookie以纯文本的形式在浏览器和服务器中传递,很容易被非法用户截获,然后进行一系列的篡改,并在Cookie的有效期内重新发送给服务器。另外,在HTTPOnly为false的情况下,Cookie信息能直接通过JS脚本读取。

cookie属性

属性说明
keycookie的键(名称)
valuecookie的值
max_agecookie被保存的时间数,单位为秒。
expires具体的过期时间,一个datetime对象或UNIX时间戳
path限制cookie只在给定的路径可用,默认为整个域名下路径都可用
domain设置cookie可用的域名,默认是当前域名,子域名需要利用通配符domain=.当前域名
secure如果设为True,只有通过HTTPS才可以用
httponly如果设为True,禁止客户端JavaScript获取cookie
  • session

session大小理论上没有限制,安全性也大于cookie,保存于服务器端。

缺陷:Session保存的东西越多,就越占用服务器内存,对于用户在线人数较多的网站,服务器的内存压力会比较大。依赖于cookie(session id保存在cookie),如果禁用cookie,则要使用URL重写,不安全创建session变量有很大的随意性,可随时调用,不需要开发者做精确地处理,所以,过度使用session变量将会导致代码不可读而且不好维护。

  • storage

sessionStorage属于会话级存储,容量相对较大,不参与请求,能够持久化存储,用于临时保存同一窗口的数据,在关闭窗口或页签之后将会删除这些数据。localStorage与sessionStorage基本一致,只不过localStorage关闭浏览器也不会被清理,所以需要手动清理,避免浪费;indexedDB也是持久化存储,不过用到的会比较少。

  • indexedDB

应用于storage数据量过大时,indexedDB没有存储限制,不过使用上不大方便。

4.ajax实现原理及使用

Ajax是一种异步请求数据的web开发技术, 其核心是浏览器提供的XMLHttpRequest对象,使得浏览器可以发出HTTP请求与接收HTTP响应,等收到XHR返回来的数据再渲染页面。

来看一下XMLHttpRequest基本使用:

//1.创建XMLHttpRequest对象,如果是IE5,IE6则需要考虑兼容性
var ajax = new XMLHttpRequest();

//2.规定请求的类型、URL、以及是否异步处理请求
ajax.open('POST',url,true);
//3.发送信息至服务器时内容编码类型,即请求头设置
ajax.setRequestHeader('Content-type','application/x-www-form-urlencoded');
//4.发送请求,send里放着请求参数,即请求体
ajax.send("name=zxp&age=18");
//5.响应服务器数据
ajax.onreadystatechange = funciton(){
	if(obj.readyState ==4 &&(obj.status == 200 || boj.status ==304)){
		....
	}
}

拓展:
1中IE5、6的兼容:var ajax=new ActiveXObject("Microsoft.XMLHTTP");  
2中如果设置的是同步处理,可以通过ajax.responseText获得字符串形式的响应数据。
2中如果是GET请求,则需要在url上拼接上对应的参数,在4中直接填写null即可
5中的readyState有五个状态值:
0,未初始化,尚未调用.open()方法;
1,启动,已经调用.open()方法,但尚未调用.send()方法;
2,发送,已经调用.send()方法,但尚未接收到响应;
3,接收,已经接收到部分响应数据;
4,完成,已经接收到全部响应数据,而且已经可以在客户端使用了;

5.axios实例

测试url:123.207.32.32:8000/home/multidata

//request.js
import axios from 'axios'
import {Message} from 'element-ui'
import store from '@/store'
import {getToken} from '@/utils/auth' //获取登录后的token

//创建请求,设置超时时间以及请求的url
const service = axios.create({
  baseURL:'xxxx',//完整的url会由axios进行拼接 = baseURL + 传入的request url
  //withCredentials:true,//跨域请求时发送cookies
  timeout:30000// 请求超时时间
})

//request请求拦截
//拦截客户端请求信息,比如登录成功后,页面中所有请求都需要携带token,用于服务端判断用户登录信息
service.interceptors.request.use(
  config=>{
    if(store.getters.token){
      config.headers['token'] = getToken()
    }
    return config
  },error =>{
    return Promise.reject(error)
  }
)

//response返回拦截,对返回的数据初步处理
service.interceptors.response.use(
  response=>{
    const res= response.data
    if(res.code !== '0000' ){
      return Promise.reject('error')
    }else{
      return res
    }
  },error=>{
    Message({
      message:'xxxx',
      type:'error'
    })
    return Promise.erject(error)
  }
)
export default service

6.轮询

ajax轮询一般分为两种。一种是设定一个定时器,时间一到就发请求,这种比较消耗资源,如:

let index = 1;

setInterval(() => {
  //发送请求
}, 1000);

//由于请求所耗时间不固定,所以可以优化为Promise + setTimeout的方式

const sleep = () => {
  return new Promise(resolve => {
    setTimeout(()=>{
        //发送请求
        resolve()
    }, 1000);
  });
}

还有一种是ajax长轮询,它需要服务器接到请求后保持连接,直到有新消息才关闭连接。实现上就是在第一次请求的时候,在成功以及失败的回调中再起发起请求。

Websocket是html5中的一个持久化协议,它最大的特点就是可以双向通信,允许服务器主动的向客户端推送信息,这种方式实时性比较强,创建连接后,可以通过websocket的数据包头部进行数据交换。

const socket = new WebSocket("ws://localhost:8080");

socket.onopen = function () {
    console.log("连接建立");
    socket.send('hello');
};

socket.onmessage = function (e) {
    console.log(e)
}

socket.onerror = function () {
    console.log("连接发生错误");
};

socket.onclose = function () {
    console.log("连接关闭");
};

使用时,一般我们会new一个websocket对象,然后用websocket对应的事件去执行,比如open建立连接、send发送数据、message接收数据、error通信异常、close关闭连接。

7.下载

  • form表单提交
function downloadFile(downloadUrl,fileName){
    //创建表单
    const formObj = document.createElement('form')
    formObj.action = downloadUrl
    formObj.method = 'get'
    formObj.style.display = 'none'
    //创建input,主要用来传参
    const formItem = document.createElement('input')
    formItem.value = fileName//传参的值
    formItem.name = 'fileName'//传参的字段名
    //插入到网页中
    formObj.appendChild(formItem)
    document.body.appendChild(formObj) 
    formObj.submit()//发送请求
    document.body.removeChild(formObj) //发送完清除掉
}
  • window.open(url)/location.href = url

接口请求后,返回对应文件,然后直接通过浏览器window.open打开下载对应的文件。但是这种方式只能是get请求,且这种方式无法带上请求的header,如果需要带token等权限控制,则不满足需求。

window.open(url)
location.href = url
  • a标签的download
<a href='test.jpg' download='test' > 下载</a>
//href为完整的url,download为下载文件的文件名
  • 二进制流

new Blob(array,options):Blob构造函数接受两个参数,一个只读的二进制文件 array是数组,成员是字符串或二进制对象,是一个由ArrayBuffer,ArrayBufferView,Blob,DOMString等对象构成的Array,所以responseType可以设置为blob或ArrayBuffer,因为返回后都需要const blob = new Blob([res.data]),处理数据; options参数是可选的,是一个配置对象,目前只有一个属性type,值类型是字符串,表示数据的MIME类型,默认为空字符串。

二进制流下载方式:需要将请求的responseType设置为blob或者ArrayBuffer(否则可能出现乱码),然后转化成blob,比如xlsx文件,可以转换数据流const blob = new Blob([res.data],{type:'application/vnd.ms-excel'}),这里的type可以根据具体的文件格式调整,具体自行百度。

//处理请求头
const exportApi = async function(params){
    try{
        let data = await axios({
            method:params.method,
            url:params.url,
            params:{ ...params.params },//get-->params ; post-->data
            responseType:"blob"
        })
        return data
    }catch(err){
        throw error
    }
}
//downloadingcb 下载中的回调
//downloadedcb 下载成功的回调

export default function download(event,_params={method:'post'},downloadingcb,downloadedcb){
    downloadingcb.call() //下载中的回调,调用此方法的第一个then
    const HttpRequest = exportApi(_params)
    HttpRequest.then(res=>{
    	//请求异常处理
        if(res.code === '500'){
            return
        }
        //请求正常处理
        const link = document.createElement('a')//创建a标签
        const blob = new Blob([res.data],{type:'application/vnd.ms-excel'})//转化数据流
        link.style.display = 'none' // 设置a标签不可见
        link.href = URL.createObjectURL(blob)//设置a标签url
        document.body.appendChild(link) //把a标签插入body页面
        link.click() //点击a标签
        document.body.removeChild(link) //移除a标签
        downloadedcb.call()
    }).catch(err=>{
        downloadedcb.call()
    })
}

最后

走过路过,不要错过,点赞、收藏、评论三连~