跨域的请求在服务端会不会真正执行?

上周在群里提了个问题,这是我平时面试经常会问到的一个问题,引起了大家非常激烈的讨论。
![](https://img-blog.csdnimg.cn/img_convert/b480fa65dbf32cdf480a3bf0457c977d.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u036f8c78&margin=[object Object]&originHeight=1091&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ude3e5a2b-b82c-43a7-b412-b0b340d9c9d&title=)
![](https://img-blog.csdnimg.cn/img_convert/56664fd788f8375e204598d30ed5aba5.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u4f258457&margin=[object Object]&originHeight=1322&originWidth=946&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ubefac596-7ad2-4b6f-ae7b-05c11bb0cbc&title=)
这个问题看似简单,但是其实这一个问题就足以看出大家对跨域的理解,如果平时只是了解了个概念, 那这个问题大概率不会答的那么好。
先揭晓一下答案,请求有的时候会被执行,有的时候不会执行。
![](https://img-blog.csdnimg.cn/img_convert/9c1049712fb976fb758ecde5bc1f7e29.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u5f0e8976&margin=[object Object]&originHeight=225&originWidth=225&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u8bdcfca9-e269-49de-a376-33b4a7edf62&title=)
那啥时候会执行,啥时候不会执行呢?其实这个问题主要要从以下几个方面去考虑:

  • 跨域究竟是谁的策略?
  • 在什么时机会拦截请求?
  • 究竟什么时候会发预检请求?
  • 如果有预检,请求什么时候会被真正执行?

跨域请求的拦截

有同学上来就答,一定不会执行的,请求在服务端就会被拦截!
这回答张口就来啊,先想想,服务端有什么责任和义务对跨域的请求做拦截呢?
首先我们俗称的跨域,也就是浏览器的 同源策略,直接看 MDN 的解释吧:
![](https://img-blog.csdnimg.cn/img_convert/759e57f51e1507227af36c0884272137.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u7f2d191b&margin=[object Object]&originHeight=518&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ud0f3e273-8f48-42e5-9a1b-bc805dc4976&title=)
人家那么大个标题告诉你了,这很明显是个浏览器的策略啊,干服务端啥事呢?
如果服务端拦截,那每个每个 Server 都要专门要为浏览器实现一个拦截策略,这根本不现实。
另外,服务端就算是想拦截,也没法判断请求是否跨域,HTTP Reqeust 的所有 Header 都是可以被篡改的,它用什么去判断请求是否跨域呢?很明显服务端心有余而力不足啊!
![](https://img-blog.csdnimg.cn/img_convert/8dea260ad1e80e71021260717c900213.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9f36166d&margin=[object Object]&originHeight=410&originWidth=440&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u96055d5b-2906-4ad7-9814-3aca008626f&title=)

在什么时候拦截

好了,知道服务端不会拦截了,有小朋友又跳出来抢答了:请求在浏览器发出去之前就被浏览器拦截了,请求根本发不出去!
![](https://img-blog.csdnimg.cn/img_convert/d6786a2d53bd54034689a154215a25ac.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ud12431e6&margin=[object Object]&originHeight=198&originWidth=198&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ub5b6235f-08de-4fcd-9a82-8ae89eb40e5&title=)
这个问题先放放,大家可能都看过《解决跨域问题的XXX种方式》这样的文章,一般文章里都会告诉你用 CORS 去解决跨域。
大概的原理就是客户端会通过服务端返回的一些 Header 去判断该请求是否允许跨域:
![](https://img-blog.csdnimg.cn/img_convert/b9791d25a946c9e89b0462e2d770a205.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ud80e9b3d&margin=[object Object]&originHeight=560&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ud5bdf42d-a6c1-419f-a08e-426ae88ab86&title=)
比如,Access-Control-Allow-Origin 告诉客户端允许请求在哪些 Origin 下被发送,这些 Header 一般都是我们配在 Server 上的。
回到上面的问题,如果请求没发出去,这个 Header 是怎么被带回来的呢?浏览器又咋知道 Server 允许请求在哪些 Origin 下跨域发送呢?
所以,我们又明确了一个信息:请求一定是先发出去,在返回来的时候被浏览器拦截了,如果请求是有返回值的,会被浏览器隐藏掉。

预检请求

那这么说,请求既然被发出去了,服务端又不会拦截,所以一定会被执行喽?
那当然不是,我们再回来把 CORS 这张图放大来看:
![](https://img-blog.csdnimg.cn/img_convert/540589859a8fd37748ea6b5558e90bde.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ue8bc91f2&margin=[object Object]&originHeight=1146&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=ud269f0cf-64bf-49eb-be3e-9dad35abb21&title=)
我们发现,在发送真正的请求之前,浏览器会先发送一个 Preflight 请求,也就是我们常说的预检请求,它的方法为 OPTIONS
这也就是为什么有的时候我们明明只发了一个请求,在 Network 里却看到两个:
![](https://img-blog.csdnimg.cn/img_convert/55dae901e48a48c843e02eecc39bc750.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=u9abefe64&margin=[object Object]&originHeight=502&originWidth=1080&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u2e7c473b-5d69-49fc-9272-80adb2fc260&title=)
预检请求有一个很重要的作用就是 询问 服务端是不是允许这次请求,如果当前请求是个跨域的请求,你可以理解为:询问 服务端是不是允许请求在当前域下跨域发送。
当然,它还有其他的作用,比如 询问 服务端支持哪些 HTTP 方法。

预检的过程

当预检请求到达服务端时,服务端是不会真正执行这个请求的逻辑的,只会在这个请求上返回一些 HTTP Header,以此来告诉客户端是不是要发送真正的请求。
如果服务端告诉客户端,请求是允许被发送的,那真正的请求才会发出去。
比如:我在 a.com 这个 origin 下,发送了 conardli.top 这个域名的请求。
那么浏览器会先向 conardli.top 发送一个预检,预检请求不会真正执行这个域名的请求,而是返回了一些 CORS Header,比如 Access-Control-Allow-Origin: a.com
这时候浏览器发现, conardli.top 的请求是允许在 a.com 下发送的,才会真正发出请求。这时服务端才会真正执行请求接口的逻辑。
那么,所有的请求都会有预检吗?当然不是。

简单请求和复杂请求

预检请求虽然不会真正在服务端执行逻辑,但也是一个请求啊,考虑到服务端的开销,不是所有请求都会发送预检的。
一旦浏览器把请求判定为 简单请求,浏览器就不会发送预检了。
浏览器判定请求是否为简单请求要同时满足以下四个条件:

  • 使用下列方法之一:
    • GET
    • HEAD
    • POST
  • 只使用了如下的安全 Header,不得人为设置其他 Header
    • text/plain
    • multipart/form-data
    • application/x-www-form-urlencoded
    • Accept
    • Accept-Language
    • Content-Language
    • Content-Type 的值仅限于下列三者之一:
  • 请求中的任意 XMLHttpRequest 对象均没有注册任何事件监听器;XMLHttpRequest 对象可以使用 XMLHttpRequest.upload 属性访问。
  • 请求中没有使用 ReadableStream 对象。

所以,如果你发送的是一个简单请求,这个请求不管是不是会受到跨域的限制,只要发出去了,一定会在服务端被执行,浏览器只是隐藏了返回值而已。
![](https://img-blog.csdnimg.cn/img_convert/f153623cf5518998ebd01546aa05a794.png#clientId=ub2602823-bcca-4&crop=0&crop=0&crop=1&crop=1&from=paste&id=ufd4cf24c&margin=[object Object]&originHeight=86&originWidth=146&originalType=url&ratio=1&rotation=0&showTitle=false&status=done&style=none&taskId=u65b9b1e6-08e5-4526-994d-315509e6165&title=)
现在,一切都清晰了吧 …

总结

最后来总结下要点:

  • 简单请求:不管是否跨域,只要发出去了,一定会到达服务端并被执行,浏览器只会隐藏返回值
  • 复杂请求:先发预检,预检不会真正执行业务逻辑,预检通过后才会发送真正请求并在服务端被执行
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值