今天自己搭了个react架子,网上找了个公开的接口,结果发现跨域了。因为接口是别人的,我没法让别人在接口上处理跨域问题,而且这个接口是post请求方式,也没发用jsop处理跨域。
一、前端处理跨域
1、利用proxy代理
特点:这种方式简单易用,不受接口类型限制,get,post等都能支持,适用于与前后端分离的项目。
使用方式:
找到package.json文件,在里面加上 proxy:代理接口地址,重新启动项目即可。
"proxy": "http://10.99.206.102:9992/api/v1"
需要注意的是,若使用本方法代理跨域,且开启了全局配置的公共接口地址,一定记得将公共接口地址配置为空。我项目一般使用的axios,需要将这个全局配置改为空。axios.defaults.baseURL = ""
随便请求一个接口,看看接口是不是走的自己电脑的ip或者localhost,若不是就证明代理跨域没成功,检查下接口配置有没有清理。
2、利用jsonp处理get请求
若只是想处理单个get请求的跨域,直接使用jsonp方式即可。这个项目中基本没用,有需要的话,自己百度看看怎么操作的。
二、node代理跨域
总体思路,就是直接让node去掉这个跨域的接口(因为服务器端不存在跨域,所以node调用接口是没毛病的),拿到结果。然后在node端抛出这个路由,前端调用这个接口就可以了。但是我的react是脚手架启动的,并不是我写的node启动的,所以端口还是不一样,这样还是有跨域问题,这时候的跨域问题就很好解决了,在后端配置下cros就好了。
当然,这种方式比较麻烦,一般工作中后台直接处理跨域就可以了,不需要单独再启动node处理跨域。这里就是记录下node一个完整的调用过程,java处理跨域也是一样开启一个cros即可。
闲话不多说,我直接把代码附上,然后把写代码过程中遇到的问题总结下。
我调用的公共接口是 图灵机器人 里的接口
接口地址:
http://www.tuling123.com/openapi/api
怎么用这个接口,可以直接看图灵机器人官网,上面说的很清楚。
我是直接用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 服务。