http的协议的跨域cors 和 options请求的一些理解 - Go语言中文社区

http的协议的跨域cors 和 options请求的一些理解


首先,这篇文章只是对http跨域阐述和options请求即预检查机制的一些理解,那么我们应该先了解什么事跨域,以及http的一些基础知识。
出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。摘自http访问控制

在前端开发中,特别是现在都是前后端分离,那么跨域问题应该在日常开发中经常遇到,而且跨域问题一般是后台解决的 。当看到以下报错时,基本就可以认定是跨域了。

Request header field Content-Type is not allowed by Access-Control-Allow-Headers in preflight response.

或者

No ‘Access-Control-Allow-Origin’ header is present on the requested resource. Origin ‘http://localhost:8989’ is therefore not allowed access.
这样的控制台报错基本就是CORS了 即跨域。不过前一种是端口号不同后一者是域名不一致。这里就要啰嗦下什么不一致会产生跨域:

  1. 协议
  2. 域名
  3. 端口号

那Options的请求又是怎么回事呢?不知道有么有同学注意到,在前端开发查看接口调用的情况的时候,经常会在控制台network的Heather看的

Request Method: OPTIONS

这里options叫做预检请求,就是查看是否可以或者允许跨域请求,以下是MDN的解释。

跨域资源共享标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站有权限访问哪些资源。另外,规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求(preflight request),从而获知服务端是否允许该跨域请求。服务器确认允许之后,才发起实际的 HTTP 请求。在预检请求的返回中,服务器端也可以通知客户端,是否需要携带身份凭证(包括 Cookies 和 HTTP 认证相关数据)。

也就是在前端真实发送请求时会发送一个预检请求,由他的返回来告知浏览器服务是否允许跨域请求,若不允许则会报CORS的错误。

这里有可能你会想如果能控制不发预检请求,是不是就可以不会跨域了呢?不知道你这样想了没有,反正我是一开始这样想的。后来感觉我还是逻辑不行(其实事觉得自己有点蠢)。
其实这是两个概念跨域就是跨域,即使你不发预请求,真实请求请求到服务器也会拒绝访问,前端即浏览器发起的预检请求,只是在发送真实请求时,先跑过去和服务器确定下,是否允许,就这个作用,再细节有可能还有其他的作用吧 。

所以接下来先简单说下跨域的服务器解决方式以node为例

// 设置跨域访问
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By", ' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

这里就是要在服务器返回头告知,接受资源请求类似的意思
,接下来说下Options什么时候回发起,

当请求满足下述任一条件时,即应首先发送预检请求:
1. 使用了下面任一 HTTP 方法:
PUT
DELETE
CONNECT
OPTIONS
TRACE
PATCH
2. 人为设置了对 CORS 安全的首部字段集合之外的其他首部字段。该集合为:
Accept
Accept-Language
Content-Language
Content-Type (but note the additional requirements below)
DPR
Downlink
Save-Data
Viewport-Width
Width
3. Content-Type 的值不属于下列之一:
application/x-www-form-urlencoded
multipart/form-data
text/plain
4. 请求中的XMLHttpRequestUpload 对象注册了任意多个事件监听器。
5. 请求中使用了ReadableStream对象。

这里简单的启动两个 以express的node服务

app.js 这个作为静态资源服务器

var express = require('express');
var app = express();
var bodyParser = require("body-parser");
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
var server = require('http').createServer(app);

app.use('/', express.static(__dirname + '/'));
server.listen(8989, function() {
    var host = server.address().address
    var port = server.address().port
    console.log("listen " + host + ":" + port);
});


app.post("/testPost", function(req, res) {
    res.json("ok");
});

app8900.js 提供接口模拟真实服务

var express = require('express');
var app = express();
var bodyParser = require("body-parser");
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
var server = require('http').createServer(app);

// 设置跨域访问
app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*");
    res.header("Access-Control-Allow-Headers", "X-Requested-With");
    res.header("Access-Control-Allow-Methods", "PUT,POST,GET,DELETE,OPTIONS");
    res.header("X-Powered-By", ' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});


app.use('/', express.static(__dirname + '/'));
server.listen(8900, function() {
    var host = server.address().address
    var port = server.address().port
    console.log("listen " + host + ":" + port);
});


app.post("/testPost", function(req, res) {
    res.json("ok");
});

这里加上了跨域的处理请求,下面我们来看脚本代码index.html

<script src="./zepto.min.js"></script>
<script>
    $.ajax({
        url: "http://localhost:8900/testPost",
        type: "post",
        contentType: "application/json;charset=utf-8",
        // contentType: "text/plain",
        data: {},
        success: function(res) {
            console.log("success", "ajax");
        },
        error: function(e) {
            console.log("error", "ajax");
        }
    });

    $.post("http://localhost:8900/testPost", function() {
        console.log("success post");
    });
</script>

分别启动app.js 和app8900.js ,访问http://localhost:8989/index.html,
会发现 console有两个请求
network显示有两个请求
options预检请求
post请求
console控制台报错ajax请求跨域

这四个图我控制了预检请求,在ajax请求的参数contentType 不同的值会产生预检请求,而如果预检请求没有允许或者认为不允许的时候,真实请求是不会发送出去的。
我来是可以解决这个跨域问题的node中,我引入了cors模块

var express = require('express');
var app = express();
var cors = require("cors");
app.use(cors());
var bodyParser = require("body-parser");
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
var server = require('http').createServer(app);



app.use('/', express.static(__dirname + '/'));
server.listen(8900, function() {
    var host = server.address().address
    var port = server.address().port
    console.log("listen " + host + ":" + port);
});


app.post("/testPost", function(req, res) {
    res.json("ok");
});

然后我们请求的时候讲请求的ajax参数换成可以发出预请求的

 $.ajax({
        url: "http://localhost:8900/testPost",
        type: "post",
        contentType: 'application/json;charset=utf-8',
        // contentType: "text/plain",
        data: {},
        success: function(res) {
            console.log("success", "ajax");
        },
        error: function(e) {
            console.log("error", "ajax");
        }
    });

结果如图会有options 预请求
这里写图片描述

若将请求参头换成
contentType: “text/plain”,//这个为规则里的一条详见MDN,则结果只要两个真实的post
这里写图片描述

源码demo项目链接资源下载CSDN
参考资料:HTTP访问控制(CORS)

版权声明:本文来源CSDN,感谢博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
原文链接:https://blog.csdn.net/sinat_23156865/article/details/81106710
站方申明:本站部分内容来自社区用户分享,若涉及侵权,请联系站方删除。
  • 发表于 2019-09-01 17:40:10
  • 阅读 ( 2919 )
  • 分类:

0 条评论

请先 登录 后评论

官方社群

GO教程

猜你喜欢