【二十一】请求封装

前言

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

面试回答

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属性

属性 说明
key cookie的键(名称)
value cookie的值
max_age cookie被保存的时间数,单位为秒。
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基本使用:

scss 复制代码
//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

javascript 复制代码
//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轮询一般分为两种。一种是设定一个定时器,时间一到就发请求,这种比较消耗资源,如:

javascript 复制代码
let index = 1;

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

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

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

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

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

ini 复制代码
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表单提交
javascript 复制代码
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等权限控制,则不满足需求。

ini 复制代码
window.open(url)
location.href = url
  • a标签的download
bash 复制代码
<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可以根据具体的文件格式调整,具体自行百度。

javascript 复制代码
//处理请求头
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()
    })
}

最后

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

相关推荐
崔庆才丨静觅6 小时前
hCaptcha 验证码图像识别 API 对接教程
前端
passerby60617 小时前
完成前端时间处理的另一块版图
前端·github·web components
掘了7 小时前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅7 小时前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅7 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅8 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment8 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅8 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊8 小时前
jwt介绍
前端
爱敲代码的小鱼8 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax