uni-app H5跨域解决办法(本地 + 服务器 环境)

5,551 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

说实话,当初为了这个跨域问题,各种博客文章都看过,千篇一律,大部分都是抄来抄去,没有一个能彻底解决问题的。uniapp官方的问答社区,只能呵呵,官方文档都写不明白,更别说找到正确的解决办法了,只能一点点摸索。

为什么会出现跨域问题以及相关机制等,这里就不说了,只重点说一下:跨域场景和解决办法!

本地环境调试 跨域解决办法

跨域场景

  1. 对于前后端分离的项目,在开发阶段,在本地调试,前端想调用后端的api,那就涉及到跨域

如果前后端项目都在本机localhost下运行,那就不涉及跨域,但是既然分离了,一般前后端也不是一个人写了

  1. 前端项目调用公共的api接口

后话:这种情况,在部署时,项目肯定不可能和公共api部署在同域服务器,也不可能同域名了,那就必须要解决如何跨域问题

解决办法

jsonp(json padding)

简单说一下原理:
利用script标签没有跨域限制的漏洞,骗浏览器这货的小伎俩...所以并不上什么高大上的新技能,只能说钻了一个空子
方法局限性:
只能处理GET请求,POST/PUT/DELETE等请求则不行!

为了更好的在项目里使用,这里封装一个公共方法,其他地方直接调用即可

'webjsonp': (url, data) => {
    return new Promise((resolve, reject) => {
            let dataString = url.indexOf('?') === -1 ? '?' : '&'
            let callbackName = `jsonpCB_${ Date.now() }`;
            url += `${ dataString }callback=${ callbackName }`
            if(data) {
                    for(let k in data) {
                            url += `&${ k }=${ data[k] }`
                    }
            }
            let scriptNode = document.createElement('script');
            scriptNode.src = url;
            window[callbackName] = (result) => {
                    result ? resolve(result) : reject('没有返回数据');
                    delete window[callbackName];
                    document.body.removeChild(scriptNode);
            }
            scriptNode.addEventListener('error', () => {
                    reject('接口返回数据失败');
                    delete window[callbackName];
                    document.body.removeChild(scriptNode);
            }, false)
            document.body.appendChild(scriptNode)
    })
}

其实就是创建一个script标签,然后把请求url塞到src属性里,利用Promise,最终将数据返回的一个过程

简单使用

webjsonp('https://apis.xx.yy.com/ws/location/v1/ip', {
    key: 'ABCDE-IIJ7P-SAHT3-QDBSK-11222',
    output: 'jsonp',
}).then(res => {
    // console.log(res);
    //....
}).catch(e => {
    console.log("error:", e);
});

如果你不想自己封装或者直接使用jsonp也是可以的

再或者直接使用第三方插件也可以,比如 vue-jsonp,这里就过多介绍,怎么个用法的文章一搜一大把

配置manifest.json文件(Proxy代理)

打开该配置文件(源码视图)

image.png 找到"h5"模块,添加如下代码

"h5" : {
  "devServer" : {
    "proxy" : {
      "/api233" : {
        "target" : "http://api.xxx.com",
        "changeOrigin" : true,    //是否跨域
        "secure" : false,         // 设置支持https协议的代理
        "pathRewrite" : {
          "^/api233" : ""       //这里一定要留空,不然有问题
        }
      },
      "api244": {
          "target" : "http://api.yyy.com",
          "changeOrigin" : true,
          "secure" : false,
          "pathRewrite" : {
             "^/api244" : ""
          }
      }//可添加多个...
    }
  }  
}

使用方法

uni.request({
        url: '/api233/xx/super_search',
        data: {
                apikey: "xxxx",
                has_coupon: "true",
                sort: "desc"
        },
        method: 'POST',
        success(res) {
                console.log("success", res);
        },
        fail(res) {
                console.log("err", res);
        },
        complete() {
                that.isLoading = false;
        }
});

无论请求是GET还是POST等,都不受限制了
这里的/api233相当于被配置文件中target的值替换掉了,差不多相当于一个变量名吧
通过浏览器中network可以看到,请求路径变成了localhost,但是其实真实请求地址还是api.xxx.com,障眼法而已

HBuilderX的内部浏览器(需要单独安装浏览器插件)

如果使用uniapp开发,那就要用HBuilderX了,但是本地测试,不习惯用他的,看控制台麻烦,还是外部浏览器比较好,还省着装插件了
总之,这个方法最简单,但我不用

服务器环境(ubuntu + nginx) 跨域解决办法

前后端项目部署在不同域名 或者 相同域名但是端口不同时,前端请求会遇到的跨域问题 对于上面使用了Proxy代理方式跨域的,部署到服务器环境后,这种方式会失效,需要在nginx重新配置一个
这个配置非常简单,找到网站的nginx配置文件,打开在如下结构的位置处添加一个localtion配置即可

http {
    server {
            ......
            location / {
                    root /var/www/html;  #Web网站程序存放目录。
                    index index.html index.htm;
            }
            //添加如下配置
            location /api233 {
                    proxy_pass http://api.xxx.com;
            }
            .....

注:配置修改完毕后,不会立即生效,还需要重启一下nginx 重启方式:直接执行 /usr/sbin/nginx -s reload(如果不知道nginx安装在哪里,可以先执行一下whereis nginx