Node.js:express编写接口.CORS跨域资源共享和JSONP请求

457 阅读6分钟

CORS跨域资源共享

接口的跨域问题

上一篇中编写的GET和POST接口,存在一个很严重的问题:不支持跨域请求。

跨域

域的概念:我们认为协议 + 域名 + 端口号均相同,那么就是同域。
跨域的概念:请求资源的域与该请求指向的资源所在的域不一样。

解决跨域问题

  • CORS:主流的解决方案(推荐)
  • JSONP:有缺陷的解决方案:只支持GET请求(很少使用)

使用cors中间件解决跨域问题

cors是Express的一个第三方中间件。通过安装和配置cors中间件,可以很方便地
解决跨域问题。

步骤:

  • 运行npm install cors安装中间件
  • 使用const cors = require('cors')导入中间件
  • 在路由之前调用app.use(cors())配置中间件

什么是CORS

CORS (Cross-Origin Resoutce Sharing,跨域资源共享)由一系列HTTP
响应头组成,这些HTTP响应头决定浏览器是否阻止前端JS代码跨域获取资源

浏览器的同源安全策略默认会阻止网页“跨域”获取资源。但如果接口服务器配置了CORS相关的HTTP响应头,就可以解除浏览器的跨域访问限制

注意事项

  • CORS主要在服务器端进行配置,客户端浏览器无须做任何额外的配置,即可请求那些开启了CORS的借口
  • CORS在浏览器中有兼容性。只有支持XMLHttpRequest Level2的浏览器,才能正常访问开启了CORS的服务端接口(例如:IE10+,Chrome4+,RireFox3.5+)

CORS 响应头部 Access-Control-Allow-

请求头和响应头的概念

请求头

请求头部由关键字/值对组成,每行一对,关键字和值用英文冒号“:”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:

  • User-Agent:产生请求的浏览器类型。
  • Accept:客户端可识别的内容类型列表。
  • Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。

响应头

响应头讯息里包含了服务器的响应讯息,如http版本,压缩方式,响应文件类型,文件编码等

Access-Control-Allow-Origin

    Access-Control-Allow-Origin: <origin>|*

其中,origin参数的值指定了允许访问该资源的外域URL。 如果指定了Access-Control-Allow-Origin字段的值为通配符*,表示允许来自任何域的请求

    //下面的字段值只允许来自http://123.com 的请求:
    res.setHeader('Access-COntrol-Allow-Origin','http://123.com')
    //下面的字段值允许来自允许来自任何域的请求:

Access-Control-Allow-Headers

默认情况下,CORS仅支持客户端向服务器发送如下的9个请求头:
Accept,Accept-Language,Content-Language,DPR,Downlink,
Save-Data,Viewport-Width,Width,
Content-Type (值仅限于text/plain,multipart/form-data,
application/x-www-form-urlencoded三者之一)

如果客户端向服务器发送了额外的请求头信息,则需要在服务器端,通过Access-Control-Allow-Headers对额外的请求头进行声明,否则这次请求会失败

//允许客户端额外向服务器发送Content-Type请求头和X-Custom-Header请求头
//注意:多个请求头之间使用英文逗号进行分割
res.setHeader('Access-Control-Allow-Headers','Content-Type'
,'X-Custom-Header')

Access-Control-Allow-Methods

默认情况下,CORS仅支持客户端发起GET,POST,HEAD请求。

如果客户端希望通过PUT,DELETE等方式请求服务器的资源,则需要在服务器端,通过Access-Control-Allow-Methods来指明实际请求所允许使用的HTTP方法

    //只允许POST、GET、DELETE、HEAD请求方法
    res.setHeader('Access-Control-Allow-Methods','POST,GET,DELETE,HEAD')
    //允许所有的HTTP请求方法
    res.setHeader('Access-Control-Allow-Methods','*')
    

CORS请求的分类

客户端在请求CORS接口时,根据请求方式和请求头不同,可以将CORS的请求分为
两大类,分别是:
  1. 简单请求
  2. 预检请求 简单请求和服务器之间只发生一次请求,预检请求则发生两次

简单请求

同时满足以下两大条件,就属于简单请求
  1. 请求方式:GET、POST、HEAD三者之一
  2. HTTP头部信息不超过以下几种字段:无自定义头部字段,Accept,Accept- Language,Content-Language,DPR,Downlink,Save-Data,Viewport-Width,Width,Content-Type (值仅限于text/plain,multipart/form-data,application/x-www-form-urlencoded三者之一)

预检请求

在浏览器与服务器正式通信前,浏览器会先发送OPTION请求进行预检,以
获知服务器是否允许该实际请求,所以这一次的OPTION请求称为"预检请
求",服务器成功响应预检请求后,才会发送真正的请求,并且携带真实数
据。(谷歌浏览器无预检请求)

只要符合以下任何一个条件的请求,都需要进行预检请求 1.请求方式为GET、POST、HEAD之外的请求Method类型 2.请求头包含自定义头部字段 3.向服务器发送了application/json格式的数据

JSONP

概念:浏览器通过<script>标签的src属性,请求服务器上的数据,同时,服务器返回一个
函数的调用。这种请求数据的方式叫做JSONP。
特点:1.JSONP不属于真正的Ajax请求,因为它没有使用XMLHttpRequest这个对象。
     2.JSONP只支持GET请求,不支持POSTPUTDELETE请求

创建JSONP接口的注意事项

如果项目中已经配置了CORS跨域资源共享,为了防止冲突,必须配置CORS中间件之前
声明JSONP接口。否则JSONP接口会被处理成CORS的接口,
    //攸县创建JSONP接口【这个借口不会被处理成CROS接口】
    app.get('/api/jsonp',(req,res)=>{})
    
    //再配置CROS中间件【后续所有接口,都会被处理成CROS接口】
    app.use(cros())
    
    //这是一个开启了CROS的接口
    app.get('/api/get',(req,res)=>{})

实现JSONP接口的步骤

  1. 获取客户端发送过来的回调函数名字
  2. 得到要通过JSONP形式发送给客户端的数据
  3. 根据前两步得到的数据,拼接出一个函数调用的字符串
  4. 把上一步拼接得到的字符串,响应给客户端的
app.get('/api/jsonp',(req,res)=>{
    //1. 获取客户端发送过来的回调函数名字
    const funcName = req.query.callback
    //2. 得到要通过JSONP形式发送给客户端的数据
    const data = {name:'张三',age:22}
    //3. 根据前两步得到的数据,拼接出一个函数调用的字符串
    const scriptStr = ${funcName}(${JSON.stringify(data)})
    //4. 把上一步拼接得到的字符串,响应给客户端的<script>标签进行解析执行
    res.send(scriptStr)
})

调用$.ajax()函数,提供JSONP的配置选项,从而发起JSONP请求

    $(#btnJSONP).on('click',function(){
        $.ajax({
            method:'GET',
            url:'http://127.0.0.1/api/jsonp',
            dataType:'jsonp', //表示发起的JSONP请求
            success:function(res){
                console.log(res)
            }
        })
    })