electron+vue2:发送请求

发布于:2025-04-12 ⋅ 阅读:(29) ⋅ 点赞:(0)

一、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
 }

网站公告

今日签到

点亮在社区的每一天
去签到