图文并茂教你模拟302接口,实现js中axios,fetch遇到302状态码后跳转的多种方案axios,fetch成功响应拦截302
前情提要
- 日常工作中,我们会使用fetch,或者axios发起请求来获取数据,但是当我们遇到一些特殊需求的时候,使用不同库之后,会得到不同的结果,例如302,308的状态码,那么我们应该怎么处理这两种情况呢?
下面我将会手把手教你实现:
- 如何使用多种方案实现前端代码+302后端接口实现页面跳转?
- fetch 发送GET 或者 POST请求如何处理 302?或者是说fetch 怎么才能拦截302?
- Axios 响应拦截 302 状态码的方案有哪些?如何实现?
基础环境准备
使用简单代码启动一个简单的nodejs后端
初始化项目
npm init
安装依赖
npm install express cors --save
模拟几个302请求
根目录下创建app.js文件,我们模拟一些302请求
var express = require("express");
var app = express();
const cors = require("cors");
//跨域请求cors
app.use(
cors({
origin: "*",
credentials: true,
})
);
// code 200 请求
app.get("/success", function (req, res) {
res.send("ok");
});
app.post("/success", function (req, res) {
res.send("ok");
});
// code 500 请求
app.get("/error500", function (req, res) {
res.sendStatus(500);
});
const urlInfo = {
baidu: "https://www.baidu.com/",
error: "http://localhost:3002/error500", // 这个接口会返回状态码500
notFound: "http://localhost:3002/notfound", // 根本就没有这个接口
success: "http://localhost:3002/success", // 200
};
app.get("/redirect-success", function (req, res) {
res.redirect(302, urlInfo.success);
});
app.post("/redirect-success", function (req, res) {
res.redirect(302, urlInfo.success);
});
app.get("/redirect-baidu", function (req, res) {
res.redirect(302, urlInfo.baidu);
});
app.post("/redirect-baidu", function (req, res) {
res.redirect(302, urlInfo.baidu);
});
app.get("/redirect-error", function (req, res) {
res.redirect(302, urlInfo.error);
});
app.post("/redirect-error", function (req, res) {
res.redirect(302, urlInfo.error);
});
app.get("/redirect-not-found", function (req, res) {
res.redirect(302, urlInfo.notFound);
});
app.post("/redirect-not-found", function (req, res) {
res.redirect(302, urlInfo.notFound);
});
var http = app.listen(3002, "127.0.0.1", function () {
var httpInfo = http.address();
console.log(`创建服务${httpInfo.address}:${httpInfo.port}成功`);
});
注意事项
- 下述状态码,我只试了302,大家可以自行修改后端代码测试其他状况哦~~
重定向状态码:
301: Moved Permanently
302: Found
303: See Other
307: Temporary Redirect
308: Permanent Redirect
启动http服务
node app.js
或者是使用supervisor来进行服务端代码热更新
supervisor使用方式(建议使用它来启动代码)
- npm官方文档
- Node Supervisor用于在程序崩溃时重新启动程序。
- 它还可以用于在*.js文件更改时重新启动程序。
npm install supervisor -g
supervisor app.js
启动成功
接口测试
前端准备
我这里用vue来举例子啦,其他框架都一样哦
创建项目
npm create vue@latest
下载依赖
cd 项目名
npm install
启动项目
npm run dev
准备基础页面
<script setup>
</script>
<template>
<main class="main">
<button>测试</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
功能1:如何使用前端代码+302后端接口实现页面跳转?
方案1 - window.location.assign(当前页跳转)
我们在项目中添加以下代码
<script setup>
const urlInfo = {
baidu: 'http://localhost:3002/redirect-baidu'
}
const toBaidu = () => {
console.log(1)
window.location.assign(urlInfo.baidu)
}
</script>
<template>
<main class="main">
<button @click="toBaidu">点击此处跳转百度</button>
</main>
</template>
方案2 - window.open(新页面打开,或者当前页打开,可以自己控制参数)
核心代码如下:(详细代码将会在后面全部粘贴出来)
window.open(urlInfo.baidu, '_blank');
方案3 - window.location.href
核心代码如下:(详细代码将会在后面全部粘贴出来)
window.open(urlInfo.baidu, '_blank');
功能1总体代码
<script setup>
const urlInfo = {
baidu: 'http://localhost:3002/redirect-baidu'
}
const toBaidu1 = () => {
window.location.assign(urlInfo.baidu)
}
const toBaidu2 = () => {
window.open(urlInfo.baidu, '_blank');
}
const toBaidu3 = () => {
window.location.href = urlInfo.baidu
}
</script>
<template>
<main class="main">
<button @click="toBaidu1">点击此处跳转百度-1</button>
<button @click="toBaidu2">点击此处跳转百度-2</button>
<button @click="toBaidu3">点击此处跳转百度-3</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
功能2:fetch 发送GET 或者 POST请求如何处理 302?或者是说fetch 怎么才能拦截302?
我们使用模拟的几个url来进行几种情况的展示,然后根据返回结果,大家就知道如何处理302情况了哦`
情况1:
<script setup>
const urlInfo = {
baidu: 'http://localhost:3002/redirect-baidu',
error: 'http://localhost:3002/redirect-error',
notFound: 'http://localhost:3002/redirect-not-found',
}
const currentUrl = urlInfo.baidu
const fetchGet = () => {
fetch(currentUrl).then(_ =>{
console.log('fetch get ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('fetch get ---catch--- current url:', currentUrl)
console.log(e)
})
}
const fetchPost = () => {
fetch(currentUrl,{method:'post'}).then(_ =>{
console.log('fetch post ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('fetch post ---catch--- current url:', currentUrl)
console.log(e)
})
}
</script>
<template>
<main class="main">
<button @click="fetchGet">Fetch-Get-302</button>
<button @click="fetchPost">Fetch-Post-302</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
情况2:
切换URL
<script setup>
const urlInfo = {
baidu: 'http://localhost:3002/redirect-baidu',
error: 'http://localhost:3002/redirect-error',
notFound: 'http://localhost:3002/redirect-not-found',
}
const currentUrl = urlInfo.error
const fetchGet = () => {
fetch(currentUrl).then(_ =>{
console.log('fetch get ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('fetch get ---catch--- current url:', currentUrl)
console.log(e)
})
}
const fetchPost = () => {
fetch(currentUrl,{method:'post'}).then(_ =>{
console.log('fetch post ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('fetch post ---catch--- current url:', currentUrl)
console.log(e)
})
}
</script>
<template>
<main class="main">
<button @click="fetchGet">Fetch-Get-302</button>
<button @click="fetchPost">Fetch-Post-302</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
我们来分析一下这种情况,302重定向的URL是返回500的url,此时我们可以得到重定向后的结果,这个时候我们就有办法处理啦~
情况3:
切换URL
<script setup>
const urlInfo = {
baidu: 'http://localhost:3002/redirect-baidu',
error: 'http://localhost:3002/redirect-error',
notFound: 'http://localhost:3002/redirect-not-found',
}
const currentUrl = urlInfo.notFound
const fetchGet = () => {
fetch(currentUrl).then(_ =>{
console.log('fetch get ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('fetch get ---catch--- current url:', currentUrl)
console.log(e)
})
}
const fetchPost = () => {
fetch(currentUrl,{method:'post'}).then(_ =>{
console.log('fetch post ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('fetch post ---catch--- current url:', currentUrl)
console.log(e)
})
}
</script>
<template>
<main class="main">
<button @click="fetchGet">Fetch-Get-302</button>
<button @click="fetchPost">Fetch-Post-302</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
情况4:
更改URL为200情况
const urlInfo = {
baidu: 'http://localhost:3002/redirect-baidu',
error: 'http://localhost:3002/redirect-error',
notFound: 'http://localhost:3002/redirect-not-found',
success: 'http://localhost:3002/redirect-success',
}
const currentUrl = urlInfo.success
总结
- 使用Fetch,302重定向的目标URL跨域的情况下,我们无法获取此时的请求具体信息,只能在catch里拿到报错结果
- 使用Fetch, 302重定向的目标URL返回状态码200,404,500的情况下,我们可以通过res.redirected准确得知此时接口是重定向,以及通过后端接口返回的res.url得到准确的重定向URL
- 综上所述,重定向目标URL正常的情况下,我们使用Fetch可以成功响应拦截 302 状态码,以及做后续的逻辑处理,因为cors的情况非常容易处理,使用代理等其他配置完全可以避免此种情况。
功能3:axios 发送GET 或者 POST请求如何处理 302?Axios 响应拦截 302 状态码的方案有哪些?如何实现?
下载依赖
npm i axios --save
编写基础代码
<script setup>
import axios from 'axios';
const urlInfo = {
success: 'http://localhost:3002/redirect-success',
baidu: 'http://localhost:3002/redirect-baidu',
error: 'http://localhost:3002/redirect-error',
notFound: 'http://localhost:3002/redirect-not-found',
}
const currentUrl = urlInfo.success
const axiosGet = () => {
axios.get(currentUrl).then(_ =>{
console.log('axios get ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('axios get ---catch--- current url:', currentUrl)
console.log(e)
})
}
const axiosPost = () => {
axios.post(currentUrl,{method:'post'}).then(_ =>{
console.log('axios post ---then--- current url:', currentUrl)
console.log(_)
}).catch(e=>{
console.log('axios post ---catch--- current url:', currentUrl)
console.log(e)
})
}
</script>
<template>
<main class="main">
<button @click="axiosGet">Axios-Get-302</button>
<button @click="axiosPost">Axios-Post-302</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
情况1:
const currentUrl = urlInfo.success
情况2:
跨域的情况下,会走catch,然后得到Network Error的报错提示语
{
"message": "Network Error",
"name": "AxiosError",
"stack": "AxiosError: Network Error\n at XMLHttpRequest.handleError (http://localhost:5173/node_modules/.vite/deps/axios.js?v=e7c1b0b9:1451:14)",
"config": {
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
},
"adapter": [
"xhr",
"http"
],
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"env": {},
"headers": {
"Accept": "application/json, text/plain, */*"
},
"method": "get",
"url": "http://localhost:3002/redirect-baidu"
},
"code": "ERR_NETWORK",
"status": null
}
情况3:
情况4:
结论
我们打印一下统一的返回数据
{
"message": "Request failed with status code 500",
"name": "AxiosError",
"stack": "AxiosError: Request failed with status code 500\n at settle (http://localhost:5173/node_modules/.vite/deps/axios.js?v=e7c1b0b9:1204:12)\n at XMLHttpRequest.onloadend (http://localhost:5173/node_modules/.vite/deps/axios.js?v=e7c1b0b9:1421:7)",
"config": {
"transitional": {
"silentJSONParsing": true,
"forcedJSONParsing": true,
"clarifyTimeoutError": false
},
"adapter": [
"xhr",
"http"
],
"transformRequest": [
null
],
"transformResponse": [
null
],
"timeout": 0,
"xsrfCookieName": "XSRF-TOKEN",
"xsrfHeaderName": "X-XSRF-TOKEN",
"maxContentLength": -1,
"maxBodyLength": -1,
"env": {},
"headers": {
"Accept": "application/json, text/plain, */*",
"Content-Type": "application/json"
},
"method": "post",
"url": "http://localhost:3002/redirect-error",
"data": "{\"method\":\"post\"}"
},
"code": "ERR_BAD_RESPONSE",
"status": 500
}
- 使用Axios,302重定向的目标URL跨域的情况下,我们无法获取此时的请求具体信息,只能在catch里拿到报错结果,报错显示信息
- 使用Axios, 302重定向的目标URL返回状态码200的情况下,我们可以通过res.request.responseURL与res.config.url进行比对来准确得知此时接口是否重定向,以及通过后端接口返回的res.request.responseURL,以及如果重定向的url是html页面的话,我们加上判断headers["content-type"]类型是否包含text/html得到准确的重定向URL
- 使用Axios, 302重定向的目标URL返回状态码404,500的情况下,我们可以通过catch 里 捕获 error.request.responseURL与error.config.url进行比对来准确得知此时接口是否重定向,以及通过后端接口返回的error.request.responseURL,以及如果重定向的url是html页面的话,我们加上判断headers["content-type"]类型是否包含text/html得到准确的重定向URL
- 综上所述,重定向目标URL正常的情况下,我们使用Axios可以成功响应拦截 302 状态码,以及做后续的逻辑处理,因为cors的情况非常容易处理,使用代理等其他配置完全可以避免此种情况。
分析图示
代码
<script setup>
import axios from 'axios';
const urlInfo = {
success: 'http://localhost:3002/redirect-success',
baidu: 'http://localhost:3002/redirect-baidu',
error: 'http://localhost:3002/redirect-error',
notFound: 'http://localhost:3002/redirect-not-found',
}
const currentUrl = urlInfo.success
const consoleLog = (_, type) => {
// 请注意下面仅限于重定向和原有url不会包含关系
if(_.request.responseURL && _.request.responseURL.indexOf(_.config.url) === -1) {
console.log(`------------------${type} --- 拦截302 ${_.config.method} 请求------------------`)
console.log('请求URL:', _.config.url)
console.log('重定向URL:', _.request.responseURL)
}
// 如果重定向的url是html页面的话,我们还可以加上判断headers["content-type"]类型是否包含text/html
}
const axiosGet = (url) => {
axios.get(url).then(_ => {
consoleLog(_, 'Then')
}).catch(e => {
consoleLog(e, 'Error')
})
}
const axiosPost = (url) => {
axios.post(url, { method: 'post' }).then(_ => {
consoleLog(_, 'Then')
}).catch(e => {
consoleLog(e, 'Error')
})
}
</script>
<template>
<main class="main">
<button @click="axiosGet(urlInfo.success)">Axios-Get-302-success</button>
<button @click="axiosPost(urlInfo.success)">Axios-Post-302-success</button>
<button @click="axiosGet(urlInfo.baidu)">Axios-Get-302-baidu</button>
<button @click="axiosPost(urlInfo.baidu)">Axios-Post-302-baidu</button>
<button @click="axiosGet(urlInfo.error)">Axios-Get-302-error</button>
<button @click="axiosPost(urlInfo.error)">Axios-Post-302-error</button>
<button @click="axiosGet(urlInfo.notFound)">Axios-Get-302-notFound</button>
<button @click="axiosPost(urlInfo.notFound)">Axios-Post-302-notFound</button>
</main>
</template>
<style scoped>
.main {
display: flex;
align-items: center;
justify-items: center;
flex-wrap: wrap;
flex-direction: column;
margin-top: 20px;
}
button {
font-size: 16px;
display: block;
margin-bottom: 15px;
cursor: pointer;
border: none;
color: hsla(160, 100%, 37%, 1);
padding: 10px;
width: 300px;
}
</style>
代码仓库
今天就写到这里啦~
- 小伙伴们,( ̄ω ̄( ̄ω ̄〃 ( ̄ω ̄〃)ゝ我们明天再见啦~~
- 大家要天天开心哦
欢迎大家指出文章需要改正之处~
学无止境,合作共赢