一、electron+axios
1、遇到的问题:在渲染进程中使用axios会出现跨域问题
2、解决办法:
1)修改渲染进程的html页面CSP规则,保留self同源,另外添加具体的接口服务地址
弊端:
- 1、修改CSP规则以允许特定的外部资源访问可能会降低应用的安全性,特别是当允许非同源的资源访问时,应用可能更容易受到跨站脚本攻击(XSS)和其他类型的安全威胁。
- 2、Electron的未来版本可能会对安全性进行进一步的增强或调整,因此依赖于当前CSP规则的配置可能会在未来的版本中失效或不再被支持
- 3、不同Electron版本或不同浏览器引擎对CSP规则的支持存在差异,另外Electron在不同的平台上运行(如Windows、macOS、Linux)等,需确保CSP规则都能支持和应用
2)使用electron代理发送请求,参考文章electron使用axios解决cors问题
缺点:这样的请求方式 在控制台是看不到请求的 调试非常不方便,无法处理复杂的请求,例如,上传文件需要传递FormData格式的数据,而Electron的IPC机制要求发送的数据必须能够被结构化克隆算法克隆(数据被序列化),例如函数、Symbol或循环引用的对象,以及某些类型的对象(File,Blob等)会导致请求报错:IpcRenderer.invoke Error: An object could not be cloned (使用ipcMain.handle发送不能被克隆)
使用: 创建一个http.js
import { ipcMain,dialog } from "electron";
import axios from "axios";
const BASE_URL = 'http://xx.xx'
const Headers = {
Accept: "application/json, text/plain, */*"
}
export const http = () => {
ipcMain.handle("http-request", async (e, { url, method, data, params,headers}) => {
const _url = BASE_URL + url;
const _data = data?data:null
const _params = params?params:null
const _method = method;
try {
let response = null;
if (_method === "POST") {
response = await axios({
method:_method,
url: _url,
data: _data,
headers: Headers,
});
} else {
response = await axios({
method:_method,
url: _url,
params: _params,
headers: Headers,
});
}
return { data: response.data };
} catch (err) {
return { error: err.message };
}
});
};
在background.js中引入
import http from './http.js'
http()
在渲染进程中使用
//this.$electron是在main.js中绑定到了Vue.prototype上面
//const electron = window.require('electron')
//Vue.prototype.$electron = electron
const res = await this.$electron.ipcRenderer.invoke('http-request', {
url: '/system/auth/login',
method: 'POST',
data: {
password: "xxxx",
username: "xxxx",
}
})
使用这种方式不会有跨域的问题,但是只能处理简单的数据请求,且不方便打印调试
3)使用fetch发送请求
是 ES6 引入的新的 API,现代浏览器基本都支持,但在一些旧版本浏览器中需要使用 polyfill 来实现兼容。
使用:创建一个http.js
class ApiClient {
constructor(baseURL, options = {}) {
this.baseURL = baseURL;
this.defaultOptions = {
timeout: 30000,
credentials: 'include',
...options
};
// 请求拦截器
this.requestInterceptors = [];
// 响应拦截器
this.responseInterceptors = [];
}
/**
* 核心请求方法
*/
async _fetch(url, options) {
// 合并配置
const mergedOptions = {
...this.defaultOptions,
...options,
headers: {
...this.defaultOptions.headers,
...options.headers
}
};
// 应用请求拦截器
for (const interceptor of this.requestInterceptors) {
await interceptor(mergedOptions);
}
// 设置超时
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), mergedOptions.timeout);
try {
const fullUrl = this.baseURL ? `${this.baseURL}${url}` : url;
const response = await fetch(fullUrl, {
...mergedOptions,
signal: controller.signal
});
clearTimeout(timeoutId);
// 应用响应拦截器
let processedResponse = response;
for (const interceptor of this.responseInterceptors) {
processedResponse = await interceptor(processedResponse);
}
if (!processedResponse.ok) {
const errorData = await this._parseError(processedResponse);
throw new HttpError(
processedResponse.status,
errorData.message || 'Request failed',
errorData
);
}
return this._parseResponse(processedResponse);
} catch (error) {
clearTimeout(timeoutId);
if (error.name === 'AbortError') {
throw new HttpError(408, 'Request timeout');
}
throw error;
}
}
// 添加拦截器
addRequestInterceptor(interceptor) {
this.requestInterceptors.push(interceptor);
}
addResponseInterceptor(interceptor) {
this.responseInterceptors.push(interceptor);
}
// 快捷方法
get(url, options = {}) {
return this._fetch(url, { ...options, method: 'GET' });
}
post(url, body, options = {}) {
return this._fetch(url, {
...options,
method: 'POST',
body: JSON.stringify(body),
headers:{
'Content-Type':'application/json'
}
});
}
put(url, body, options = {}) {
return this._fetch(url, {
...options,
method: 'PUT',
body: JSON.stringify(body)
});
}
delete(url, options = {}) {
return this._fetch(url, { ...options, method: 'DELETE' });
}
// 文件上传
upload(url, formData, options = {}) {
const headers = {
...options.headers,
};
delete headers['Content-Type'];
return this._fetch(url, {
...options,
method: 'POST',
body: formData,
headers
});
}
// 解析响应
async _parseResponse(response) {
const contentType = response.headers.get('content-type') || '';
if (contentType.includes('application/json')) {
return response.json();
}
if (contentType.includes('text/')) {
return response.text();
}
return response.blob();
}
// 解析错误
async _parseError(response) {
try {
return await response.json();
} catch {
return { message: response.statusText };
}
}
}
const Http = new ApiClient(ELECTRON_CONFIG.AXIOS_URL);
// 添加请求拦截器
Http.addRequestInterceptor(async (options) => {
//请求拦截添加token
});
// 添加响应拦截器
Http.addResponseInterceptor(async (response) => {
return response;
});
export default Http
在main.js中引入
import Http from '@/Http/http'
Vue.prototype.$Http = Http
发送请求
const res = await this.$Http.upload('xxx',data)
if(res.code == 500){
this.$message.error(res.msg)
return
}