美文网首页
react+axios用node代理解决跨域

react+axios用node代理解决跨域

作者: lemonzoey | 来源:发表于2018-04-20 18:37 被阅读0次

    今天自己搭了个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 <div>
                <header className='header'>我是头部信息</header>
                <div>{one}</div>
            </div>  
        }
    }
    
    ReactDOM.render(
        <Layout/>,
        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[‘title’] 可以获取到 title 的值,_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代理解决跨域

          本文链接:https://www.haomeiwen.com/subject/oruzkftx.html