實戰前端跨域問題

Mango在掘金發表於2019-04-11

下面我以簡單的兩臺node伺服器來說明如何使用nginx進行前端跨域訪問。

  1. node1伺服器 在localhost:8083上啟動
const app = express();
app.get('/web/users',(req, res)=>{
    res.json([{name:"張三",age:12},{name:"李四",age:14}]);
    res.end()
})
app.listen(process.env.PORT || 8083);
複製程式碼

同域下的前端程式碼只需呼叫

function getUsers() {
        var xhr=new XMLHttpRequest();
        xhr.open('GET', 'http://localhost:8083/web/users');
        xhr.send(null);
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && xhr.status == 200) {
              alert(xhr.responseText);
            }else {
              alert(xhr.statusText);
            }
        }
}
複製程式碼

這裡有一點需要關注的是,前後端程式碼處在同一個域下,xhr.open() url路徑寫成下面這樣也是可以的,它會預設請求到http://localhost:8083/web/users

xhr.open('GET', '/web/users');
複製程式碼

下面我們將node1伺服器中的web/users介面刪除:

const app = express();
app.listen(process.env.PORT || 8083);
複製程式碼

前端此時自然無法訪問後臺的web/users了,將報一個404錯誤。

下面增加一個node2伺服器,在localhost:8085上啟動,同時我們將原先在8083上刪除的web/users介面搬遷到8085上:

const app = express();
app.get('/web/users',(req, res)=>{
    res.json([{name:"張三",age:12},{name:"李四",age:14}]);
    res.end()
})
app.listen(process.env.PORT || 8085);
複製程式碼

由於8085實現了這個介面,我們嘗試在原先8083埠下的ajax呼叫它試試:

 xhr.open('GET', 'http://localhost:8085/web/users');
複製程式碼

如我們所料,瀏覽器阻止了此次行為,並丟擲一個跨域錯誤。

實戰前端跨域問題

難道就無法訪問那個介面了嗎?其實伺服器之間和伺服器之間是可以相互呼叫的,阻止跨域訪問只是在瀏覽器端做的限制而已。

下面我通過兩種方式來實現如何訪問到8085上的web/users介面。

1 . node直接作為代理訪問

原理就是交由8083後端去訪問8085埠介面,訪問完成交給前端
8083node後端實現程式碼:

npm install request --save // 需要安裝一個http request模組
複製程式碼
const app = express()
const request = require('request')
// 訪問此介面時通過request模組去訪問8085 再返回給前端。
app.get('/web/users',(req, res)=>{
    var url='http://localhost:8085'+req.url
    console.log(url) // http://localhost:8085/web/users
    req.pipe(request(url)).pipe(res);
})
複製程式碼

2. jsonp方式訪問

就是通過script 的src,向伺服器請求資料,且這不受同源策略限制;然後伺服器將相應的資料放入指定的函式回撥名中,返回給前端。說的有點繞,下面通過例項講解: 8083前端請求8085,這裡已經不再是ajax請求了,而是直接載入8085上資源。

 <script>
       function getUsers(data) {
         alert(data)
       }
    </script>
    <script src="http://localhost:8085/jsonp?callback=getUsers"></script>
複製程式碼

上述程式碼第一個script標籤定義了一個函式getUsers 但是並沒有執行,只是定義了而已,要想有執行能力,需要

getUsers(data) 
複製程式碼

所以我們要讓第二個標籤script src="http://localhost:8085/jsonp?callback=getUsers" 返回getUsers(data)內容即可,這樣第一個標籤內定義的函式就可以執行了。返回介面內容只需要放到函式引數裡即可
後臺8085實現:

const app = express();
const querystring=require('querystring')
const url=require('url')

// 處理前端jsonp請求
app.get('/jsonp',(req,res)=>{
  var qs = querystring.parse(req.url.split('?')[1]); //{callback:'getUsers'}
  var users=JSON.stringify([{name:"張三",age:12},{name:"李四",age:14}]) // 注意需要傳成字串格式
  var callback=`${qs.callback}(${users})`
  res.end(callback)
})
複製程式碼

前端返回

實戰前端跨域問題

是不是有點黑魔法的味道!但是缺點也是明顯的,首先通過此方式有一定安全性的,通過callback後加一些亂七八糟的東西可能會有xss攻擊,最主要的是jsonp不支援post方法。

3 .nginx反向代理器

此方式即類似於第一種node代理方式,也是通過伺服器和伺服器之間通訊,只是不需要8083下後臺伺服器自己去訪問8085,而是專門交給nginx,為什麼?姑且認為nginx更加專業吧! nginx配置

server {
        listen 8007;          # nginx啟動埠,需要訪問這個埠才能夠代理
        server_name localhost:8083; # 需要代理的伺服器
        location / {          # 如果你訪問127.0.0.1/8002/的話nginx去請求
          proxy_pass http://localhost:8083;
        }
        location /web { #訪問/web/aa時會對映成http://localhost:8085/web/aa;
          proxy_pass   http://localhost:8085;
        }
    }
複製程式碼

8083請求路徑

xhr.open('GET', '/web/users');
複製程式碼

實戰前端跨域問題

未完待續...

相關文章