今天自己搭了個react架子,網上找了個公開的接口,結果發現跨域了。因為接口是别人的,我沒法讓别人在接口上處理跨域問題,而且這個接口是post請求方式,也沒發用jsop處理跨域。
一、前端處理跨域
1、利用proxy代理
特點:這種方式簡單易用,不受接口類型限制,get,post等都能支援,适用于與前後端分離的項目。
使用方式:
找到package.json檔案,在裡面加上 proxy:代理接口位址,重新啟動項目即可。
"proxy": "http://10.99.206.102:9992/api/v1"
proxy
需要注意的是,若使用本方法代理跨域,且開啟了全局配置的公共接口位址,一定記得将公共接口位址配置為空。我項目一般使用的axios,需要将這個全局配置改為空。axios.defaults.baseURL = ""
随便請求一個接口,看看接口是不是走的自己電腦的ip或者localhost,若不是就證明代理跨域沒成功,檢查下接口配置有沒有清理。
2、利用jsonp處理get請求
若隻是想處理單個get請求的跨域,直接使用jsonp方式即可。這個項目中基本沒用,有需要的話,自己百度看看怎麼操作的。
二、node代理跨域
總體思路,就是直接讓node去掉這個跨域的接口(因為伺服器端不存在跨域,是以node調用接口是沒毛病的),拿到結果。然後在node端抛出這個路由,前端調用這個接口就可以了。但是我的react是腳手架啟動的,并不是我寫的node啟動的,是以端口還是不一樣,這樣還是有跨域問題,這時候的跨域問題就很好解決了,在後端配置下cros就好了。
當然,這種方式比較麻煩,一般工作中背景直接處理跨域就可以了,不需要單獨再啟動node處理跨域。這裡就是記錄下node一個完整的調用過程,java處理跨域也是一樣開啟一個cros即可。
閑話不多說,我直接把代碼附上,然後把寫代碼過程中遇到的問題總結下。
我調用的公共接口是 圖靈機器人 裡的接口
接口位址:
我是直接用create-react-app搭建的項目,然後把src下面的目錄都删掉,自己建了兩個檔案,index.js和index.css
最終的檔案目錄結構如下:
檔案目錄
src/index.js檔案如下:
import React from 'react'
import ReactDOM from 'react-dom'
import axios from 'axios'
import './index.css'
class Layout extends React.Component{
constructor(){
super()
this.state={
content:'',
value:'zoey'
}
}
componentDidMount(){
let data1
axios.post('http://www.tuling123.com/openapi/api',{
info:'今天我最美',
userId:1234
}).then(data=>{
console.log(data)
data1 = data
})
this.setState({
content:data1
})
}
render(){
debugger
const obj = this.state.content
let one = []
for( var item in obj){
one.push( item=='text'? obj[item]:'')
}
return
我是頭部資訊
{one}
}
}
ReactDOM.render(
,
document.querySelector('#root')
)
這裡就會發現報錯
跨域
因為同源政策,導緻這裡跨域,我就開始搭建了個node伺服器,代碼如下:
建了個檔案夾server,裡面建立3個檔案server.js routes.js home.js
server/server.js的代碼:
const Koa= require('koa')
const app = new Koa()
const routes = require('./routes.js');
//路由
app.use(routes());
app.listen(3001,()=>{
console.log('port is running at 3001')
})
server/routes.js的代碼
const compose = require('koa-compose');
const Router = require('koa-router');
var router = new Router();
const Home = require('./home');
router.get('/api/index', Home.index)
// app.use(router.routes()).use(router.allowedMethods());
module.exports = (ctx, next) => compose([router.routes(), router.allowedMethods()]);
server/home.js的代碼:
const axios = require('axios')
class Home {
static async index(ctx,next){
const {info,userId} =ctx.request.query
const {data} = await axios.post('http://www.tuling123.com/openapi/api',{
key:'c9d1eb9811e648a49ece24b7cb1065e9',
info:info,
userId:userId
})
console.log(data)
ctx.body=data
}
}
module.exports = Home
然後啟動這個server,為了友善我就沒配置,直接手動切換到server目錄下,node server.js啟動項目,這樣這個伺服器就抛出了可以localhost:3001/api/index這個接口了,這個接口傳回的結果就是我們所需的了。
現在改改src/index.js中componentDidMount中的代碼
componentDidMount(){
let data
//注意,這裡3001 就是node啟用的端口
axios.get('http://localhost:3001/api/index',{
params:{ info:'今天我最美',
userId:1234}
}).then(req=>{
{data} = req
})
this.setState({
content:data
})
}
這裡你就發現一個問題了,又一次出現了跨域,那現在這個跨域就很好解決了,配置下服務端代碼就可以了,我直接在server/server.js中用了koa-cros這個插件就可以啦,這裡注意,cors一定要放在路由的上面。
const Koa= require('koa')
const app = new Koa()
const routes = require('./routes.js');
const cors = require('koa2-cors');
//運作跨域
app.use(cors());
//路由
app.use(routes());
app.listen(3001,()=>{
console.log('port is running at 3001')
})
配置好了以後,又發現data取不到資料,這裡因為axios是異步的,資料還沒有擷取,頁面已經渲染完成了。因為axios傳回的是一個promise,是以這裡可以把componentDidMount改成異步等待的方法,取得data的值
async componentDidMount(){
//注意,這裡3001 就是node啟用的端口
const {data}= await axios.get('http://localhost:3001/api/index',{
params:{ info:'今天我最美',
userId:1234}
})
this.setState({
content:data
})
}
這樣資料就能顯示了
在調用過程中發現我對于post方式裡的Content-Type 的參數不是很清楚,下面我總結下Content-Type 幾種常用的參數的使用場景:(這裡為轉載)
application/x-www-form-urlencoded
這應該是最常見的 POST 送出資料的方式了。浏覽器的原生 form 表單,如果不設定 enctype 屬性,那麼最終就會以 application/x-www-form-urlencoded 方式送出資料。請求類似于下面這樣
POST http://www.example.com HTTP/1.1
Content-Type: application/x-www-form-urlencoded;charset=utf-8
title=test&sub%5B%5D=1&sub%5B%5D=2&sub%5B%5D=3
首先,Content-Type 被指定為 application/x-www-form-urlencoded;其次,送出的資料按照 key1=val1&key2=val2 的方式進行編碼,key 和 val 都進行了 URL 轉碼。大部分服務端語言都對這種方式有很好的支援。例如 PHP 中,
_POST[‘sub’] 可以得到 sub 數組。
很多時候,我們用 Ajax 送出資料時,也是使用這種方式。例如 JQuery 和 QWrap 的 Ajax,Content-Type 預設值都是「application/x-www-form-urlencoded;charset=utf-8」。
multipart/form-data
這又是一個常見的 POST 資料送出的方式。我們使用表單上傳檔案時,必須讓 form 的 enctyped 等于這個值。直接來看一個請求示例:
POST http://www.example.com HTTP/1.1
Content-Type:multipart/form-data; boundary=----WebKitFormBoundaryrGKCBY7qhFd3TrwA
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="text"
title
------WebKitFormBoundaryrGKCBY7qhFd3TrwA
Content-Disposition: form-data; name="file"; filename="chrome.png"
Content-Type: image/png
PNG ... content of chrome.png ...
------WebKitFormBoundaryrGKCBY7qhFd3TrwA--
這個例子稍微複雜點。首先生成了一個 boundary 用于分割不同的字段,為了避免與正文内容重複,boundary 很長很複雜。然後 Content-Type 裡指明了資料是以 mutipart/form-data 來編碼,本次請求的 boundary 是什麼内容。消息主體裡按照字段個數又分為多個結構類似的部分,每部分都是以 –boundary 開始,緊接着内容描述資訊,然後是回車,最後是字段具體内容(文本或二進制)。如果傳輸的是檔案,還要包含檔案名和檔案類型資訊。消息主體最後以 –boundary– 标示結束。關于 mutipart/form-data 的詳細定義,請前往 rfc1867 檢視。
這種方式一般用來上傳檔案,各大服務端語言對它也有着良好的支援。
上面提到的這兩種 POST 資料的方式,都是浏覽器原生支援的,而且現階段原生 form 表單也隻支援這兩種方式。但是随着越來越多的 Web 站點,尤其是 WebApp,全部使用 Ajax 進行資料互動之後,我們完全可以定義新的資料送出方式,給開發帶來更多便利。
application/json
application/json 這個 Content-Type 作為響應頭用來告訴服務端消息主體是序列化後的 JSON 字元串。由于 JSON 規範的流行,除了低版本 IE 之外的各大浏覽器都原生支援 JSON.stringify,服務端語言也都有處理 JSON 的函數,使用 JSON 不會遇上什麼麻煩。
JSON 格式支援比鍵值對複雜得多的結構化資料,這一點也很有用。
例如下面這段代碼:
var data = {'title':'test', 'sub' : [1,2,3]};
$http.post(url, data).success(function(result) {
...
});
最終發送的請求是:
POST http://www.example.com HTTP/1.1
Content-Type: application/json;charset=utf-8
{"title":"test","sub":[1,2,3]}
這種方案,可以友善的送出複雜的結構化資料,特别适合 RESTful 的接口。各大抓包工具如 Chrome 自帶的開發者工具、Firebug、Fiddler,都會以樹形結構展示 JSON 資料,非常友好。但也有些服務端語言還沒有支援這種方式,例如 php 就無法通過 $_POST 對象從上面的請求中獲得内容。這時候,需要自己動手處理下:在請求頭中 Content-Type 為 application/json 時,從 php://input 裡獲得原始輸入流,再 json_decode 成對象。
text/xml
它是一種使用 HTTP 作為傳輸協定,XML 作為編碼方式的遠端調用規範。XML-RPC 協定簡單、功能夠用,各種語言的實作都有。JavaScript 中,也有現成的庫支援以這種方式進行資料互動,能很好的支援已有的 XML-RPC 服務。