api.js
//封装ajax方法
import $g from "../sg";//vue项目使用
import $ from 'jquery';//(提示:原生开发页面请前往https://jquery.com下载最新版jQuery)
import { Message } from "element-ui";//element项目使用
// import axios from "axios";
// 不同环境的api配置项________________________________________________________________________________
let APIs = [
{ label: '师大外网服务器', origins: ['mzjyzyk.ynnu.edu.cn'], API_ROOT_URL: `https://mzjyzyk.ynnu.edu.cn/api`, isProduct: true, },
{ label: '师大内网服务器', origins: ['10.10.4.87'], API_ROOT_URL: `http://10.10.4.87:30107/api`, },
{ label: '维度云线上测试服务器运行', origins: ['wedoyun.cn'], API_ROOT_URL: `//sr.wedoyun.cn:82/api`, },
{
label: '本地个人开发环境', origins: ["localhost", "127.0.0.1", "192.168.", "172.20.", "0.0.0.0",], isLocal: true,
API_ROOT_URL: "//192.168.1.108:9088/sr", //鲁宝键的IP
// API_ROOT_URL = "//192.168.7.95:9088/sr", //无忌的IP
API_ROOT_URL: "//sr.wedoyun.cn:82/api", //测试服务器的IP
},
];
let API = APIs.find(({ origins }) => origins.some(v => location.href.includes(v))) || APIs[0];
let API_ROOT_URL = API.API_ROOT_URL;
//一些前提条件和方法________________________________________________________________________________
localStorage.sgAPI && (API_ROOT_URL = localStorage.sgAPI);//如果localStorage.sgAPI存在,优先用这个前缀路径接口访问所有接口
//跳转到登录页面
let jumpLoginPage = ({ url, } = {}) => {
url && console.log(`导致跳转到登录页面的接口路径`, url);
$g.cookie.clearAll();//清空所有缓存(需要引入sg.js)
Object.keys(localStorage).forEach(k => delete localStorage[k]);//清空对象键、值
location.hash.includes('/login') || location.replace(`${$g.getWebURLBeforeHash()}#/login?url=${encodeURIComponent(location.href)}`);
}
// 普通报错提示
let sgAlert = (d = `很抱歉,服务太火爆了,请您稍后再试!`) => Message ? Message({ message: d, dangerouslyUseHTMLString: true, showClose: true, }) : alert(d.replace(/\<br\>/g, `\n\r`));
// 接口报错提示
let errAlert = (url, d) => sgAlert(`💡可能是后端报错:<br>接口地址👉${url}<br>返回报文👉${JSON.stringify(d, null, 4)}`);
// 本地接口提示
$g.getUrlParam('isLocal') && (API.isLocal = true);//手动添加开发者开发模式
API.isLocal && sgAlert(`温馨提示:当前处于开发模式,接口访问地址为<b style="color: #67C23A;" >${API_ROOT_URL}</b>`);
//主体请求________________________________________________________________________________
export default {
// 共享变量、方法
share: {
// 一些共享出去的变量
$g,
isLocal: API.isLocal,//是否本地环境
isProduct: API.isProduct,//是否生产环境
API_ROOT_URL,//接口根路径
imgPrePath: `${API_ROOT_URL}/`,//图片前缀
// 一些共享出去的方法
jumpLoginPage,
},
//API FUNCTION ________________________________________________________________________________
do(url, data, r, otherConfig = {}) {
if (!r) return sgAlert(`注意检查接口${url}的传参“r”是否写成了“doing”`);
// delete (data || {}).sgLog;//清除调试状态下的接口标记说明字段
let type = otherConfig.type;//请求方式,默认POST
type || (type = 'POST');
//noToken=true代表该接口无需保持登录状态就可以获取信息,譬如一些公共对外开放的接口
let token = localStorage.token; //获取token
if (!otherConfig.noToken) {
//判断如果本地没有token,否者直接跳转到登录页面
if (!token) return jumpLoginPage({ url });
}
// 建议:一般情况就不要设置headers了,ajax会根据data结构自动判断类型
let headers = {
// withCredentials: true ,// 这里可以设置withCredentials
// 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',//FormData方式提交数据
// 'Content-Type': 'application/json;charset=UTF-8',//报跨域错误
...otherConfig.headers,
};
//判断是否需要令牌字符串
if (!otherConfig.noToken) {
token && (headers["kkToken"] || (headers["kkToken"] = token));
data || (data = {});
}
// 判断是否为下载接口,防止下载导出文件乱码
let xhrFields = otherConfig.isDownload ? { xhrFields: { responseType: "arraybuffer" } } : null;
// 判断是否为上传接口
let contentType = {};
if (otherConfig.isUpload) {
/*
【警告!千万不要设置请求头格式!!! 用<form>表单上传的时候, 请求自动会设置为multipart/form-data; boundary=----WebKitFormBoundary****************】
如果是用的ajax请求上传File文件必须设置最外层contentType:false,且删除headers中的“Content-Type”,
否则就会导致提示跨域或传参格式没有序列化变成类似:
------WebKitFormBoundaryxWRglNw6MXxmBjr2
Content-Disposition: form-data; name: "字段名1"
EBE29C03DA9E4FB4A050F4D820A834BC
------WebKitFormBoundaryxWRglNw6MXxmBjr2
Content-Disposition: form-data; name="字段名2"
这样的格式。
ajax正确参数如下:
$.ajax({
...
headers:{不要有“Content-Type”},
processData:false,//必须是false
contentType:false,//必须是false
...
})
*/
data = $g.convertObejct2FormData(data);//转换为multipart/form-data数据格式
contentType = { contentType: false };//如果不设置就会导致提示跨域或传参格式没有序列化变成类似 ------WebKitFormBoundary... 的分隔请求体
}
// 序列化设置(是否将数据转换为查询字符串的形式发送给服务器,默认情况下,processData的值为true)
let processData = true;
(data instanceof FormData) && (processData = false);
(typeof otherConfig.processData !== 'undefined') && (processData = otherConfig.processData);
// 精简别名
r.s && (r.success = r.s); //响应请求成功
r.f && (r.fail = r.f); //响应请求失败
r.e && (r.error = r.e); //响应请求报错
r.l && (r.loading = r.l, r.loading.show && r.loading.show()); //加载
$.ajax({
timeout: 24 * 60 * 60 * 1000, //设置默认超时时间24小时
type,
url: otherConfig.isCompleteURL ? url : `${API_ROOT_URL}/${url}`,//使用完整接口路径
data,//发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。查看 processData 选项说明以禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:["bar1", "bar2"]} 转换为 '&foo=bar1&foo=bar2'。
headers,//请求头
...xhrFields,//设置XMLHttpRequest对象的属性
...contentType,//默认值: "application/x-www-form-urlencoded"。发送信息至服务器时内容编码类型。 默认值适合大多数情况。如果你明确地传递了一个 content-type 给 $.ajax() 那么它必定会发送给服务器(即使没有数据要发送)。
processData,//默认值: true。默认情况下,通过data选项传递进来的数据,如果是一个对象(技术上讲只要不是字符串),都会处理转化成一个查询字符串,以配合默认内容类型 "application/x-www-form-urlencoded"。如果要发送 DOM 树信息或其它不希望转换的信息,请设置为 false。
beforeSend: xhr => {
/* 发送请求前运行的函数。
发送请求前可修改 XMLHttpRequest 对象的函数,如添加自定义 HTTP 头。
XMLHttpRequest 对象是唯一的参数。
这是一个 Ajax 事件。如果返回 false 可以取消本次 ajax 请求。
*/
},
success: d => {
if (url.toLocaleLowerCase().includes(`download`) && !otherConfig.hasOwnProperty('isDownload') && typeof d === 'string') return sgAlert(`注意检查接口${url}的“otherConfig”参数是否加入了“isDownload:true”配置"`);
if (!otherConfig.isDownload && typeof d === 'string') return sgAlert(`请联系后端开发人员给接口${url}添加@RequestMapping(value=...produces="application/json)"`);
// typeof d === 'string' && (d = JSON.parse(d));//如果是字符串返回值,则转换为JSON格式(如果用这句话就会纵容后端开发,导致以后复杂的返回报文也用字符串,会有问题!)
r.loading && r.loading.close && r.loading.close(); //关闭加载
if (otherConfig.isDownload) return r.success(d); //如果是下载,直接返回文档流
switch (d.success) {
case true: //登录成功
r.success && r.success((otherConfig.isGetAllData || d.hasOwnProperty('totalCount')) ? d : (d.hasOwnProperty('data') ? d.data : d));
break;
case false: //登录失败(当token密码失效的时候)
r.fail ? r.fail(d) : sgAlert(d.msg);
break;
}
switch (d.code) {
case 403: //登录失败(当token密码失效的时候)
case 404: //登录失败(当token密码失效的时候)
jumpLoginPage(); r.fail ? r.fail(d) : sgAlert(d.msg);
break;
case -2: //扫码失败
case -1: //请求失败
r.fail ? r.fail(d) : sgAlert(d.msg);
break;
}
},
error: d => {
//请求报错
if (d.status == 403) { jumpLoginPage(); return r.error ? r.error(d) : sgAlert(JSON.parse(d.responseText).msg); }
r.loading && r.loading.close && r.loading.close(); //关闭加载
r.error ? r.error(d) : errAlert(url, d);
// console.log("【报错】" + JSON.stringify(d, null, 4),d);
},
complete: (xhr, status) => {
//请求完成时运行的函数(在请求成功或失败之后均调用,即在 success 和 error 函数之后)。
}
});
},
}
sd.js
//公共接口定义2024.11.22----------------------------------------
import $api from "./api";
import $g from "./sg";//vue项目使用
export default {
...$api.share,
//【通用接口demo】________________________________________________________________________________
API_NAME({ data, r, config }) { $api.do(`${API_ROOT_URL}/xxx/xxx/xxx`, data, r, config); },
//提交用例________________________________________
get({ r }) { $api.do(`${API_ROOT_URL}/get.do`, null, r, { isGetAllData: true, type:`get`, }); },
post({ data, r }) { $api.do(`${API_ROOT_URL}/post.do`, data, r); },
login({ data, r }) { $api.do(`${API_ROOT_URL}/post.do`, data, r, { noToken: true }); },
download({ data, r }) { $api.do(`${API_ROOT_URL}/post.do`, data, r, { isDownload: true }); },
}