react+axios用node代理解决跨域

今天自己搭了个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即可。

闲话不多说,我直接把代码附上,然后把写代码过程中遇到的问题总结下。
我调用的公共接口是 图灵机器人 里的接口

接口地址:

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 服务。

你可能感兴趣的:(react+axios用node代理解决跨域)