https://zhuanlan.zhihu.com/p/96735100
React跨域
解決方案在二,想簡單瞭解下跨域的可讀一。
我們由於項目需要經常會需要對不同域名、不同子域的網站接口發起請求,有時甚至是對於同一域名的不同端口發起請求,此時我們經常看到以下報錯:
Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS
policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
是的,錯誤的原因就是你跨域了。剛碰到跨域問題時在網上尋找了大半天,不是報錯就是無法跨域轉發。最後才發現是react新版本更新了相關規則的緣故。記述下來供大家參考。
一、爲什麼會有跨域問題?
看到網上舉的一個形象例子,先設想下,如果允許跨域,那麼黑衣人是不是可以在自己的網頁上把請求轉發給其他網站?例如,黑衣人在自己的頁面設計了一個跨域請求到某錢堆的網址,當用戶訪問黑衣人網址時,瀏覽器按照黑衣人設計去訪問了錢堆(還攜帶了用戶在錢堆那兒的cookie)......之後,之後警察叔叔們又要加班了。還有諸如此類許多安全隱患。所以後來的瀏覽器都開始實行同源策略。
同源策略,其實就是隻允許相同協議+域名+端口號(如存在)的HTTP請求互相訪問。這麼理解其實就夠了。關於跨域資源共享標準( cross-origin sharing standard )CORS的詳細內容,大家可參考這篇文章:鏈接
本文主要講React框架下怎麼解決跨域問題。
二、怎麼解決跨域問題?
這裏我給出兩種React的跨域解決方案(React16.9親測可行),第一種比較實用,第二種需要服務端協調。
1.http-proxy-middleware
網上一大片說直接在package.json中配置proxy的,這個方法已經失效很久了。官方給出的新版本解決方案需要藉助http-proxy-middleware這個包:
1. npm install http-proxy-middleware
2. src目錄下創建setupProxy.js,配置如下:
const proxy = require('http-proxy-middleware')
module.exports = function (app) {
// proxy第一個參數爲要代理的路由
// 第二參數中target爲代理後的請求網址,changeOrigin是否改變請求頭,其他參數請看官網
app.use(proxy('/cityjson', {
target: 'http://pv.sohu.com',
changeOrigin: true
}))
}
3. 測試一下:
//頁面代碼:
import React,{ useEffect,useState } from 'react'
import axios from 'axios';
const App = function () {
const [ip,setIp] = useState()
useEffect(()=>{
(async function f(){
let res = await axios.get('/cityjson') //這裏使用搜狐的ip信息查詢接口
setIp(res.data.toString())
})()
},[])
return (
<h1>獲取的IP信息:{ip}</h1>
)
}
export default App
成功獲取!
2.藉助服務端配置
其實有過服務端開發經驗的同學應該一眼就能看出,上面配置setupProxy.js的樣子像極了node服務端使用中間件的樣子。再看看,http-proxy-middleware,middleware就是中間件的意思。
好了,大概知道了,其實新版本的更新就是想把跨域請求放在服務端去做,而削弱前端跨域的能力(大概是爲了安全吧,個人臆想)
在服務端配置的話,只需要在服務端中間件文件裏插入http-proxy-middleware即可。express框架和上面的配置基本一樣,koa的話參考以下這樣:
const Koa = require('koa')
const proxy = require('http-proxy-middleware')
const app = new Koa()
//跨域代理
app.use(async (ctx, next) => {
if(ctx.url.startsWith('/cityjson')) {
ctx.respond = false
return proxy({
target: 'http://pv.sohu.com', // 服務器地址
changeOrigin: true,
})(ctx.req, ctx.res, next)
}
return next()
})
......