文章目录
- 前言
- 一、跨域问题
- 1.是什么
- 2.跨域的特征(跨域报错)
- 二、解决跨域问题的方法
- 1.golang解决跨域问题
- 2.简单请求
- 3.非简单请求过程分析(复杂请求):
- 三、状态码设置为200依旧出错
前言
今天中午在部署golang与vue搭建的一个项目时,因为将项目部署到了云端的Docker容器内
期间涉及到了一些跨域问题,本以为在后端配置一下跨域即可,没想到最后是因为非简单请求跨域的规范导致出错
本文章会介绍跨域的基础概念,解决跨域的方法都有什么,golang如何解决跨域问题,简单与非简单请求的基本概念及规定。
一、跨域问题
1.是什么
由于浏览器的同源策略限制,进而产生跨域拦截问题。同源策略是浏览器最核心也最基本的安全功能;所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)。
同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略在解决浏览器访问安全的同时,也带来了跨域问题,当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域。
2.跨域的特征(跨域报错)
Access to XMLHttpRequest at 'http://43.143.232.114:8080/login' from origin 'http://localhost:8081' has
been blocked by CORS policy: Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested resource.
二、解决跨域问题的方法
常见的方法有三种:前端对脚手架文件进行配置、nginx进行转发、后端写相应的字段配合
在解决跨域问题之前需要先了解一下简单请求与非简单请求,如果不搞明白一次请求都干了什么那么问题将得不到很好的解决。
1.golang解决跨域问题
这里是原生go解决跨域请求的方法,框架的话与这个差不多。知道了方法还需知道是因为什么,接着往下看。
resp.Header().Set("Content-Type", "application/json")
resp.Header().Set("Access-Control-Allow-Origin", "*") //允许访问所有域
// 必须,设置服务器支持的所有跨域请求的方法
resp.Header().Set("Access-Control-Allow-Methods", "POST, GET, PUT, DELETE, OPTIONS")
// 服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
resp.Header().Set("Access-Control-Allow-Headers", "content-type")
// 可选,设置XMLHttpRequest的响应对象能拿到的额外字段
resp.Header().Set("Access-Control-Expose-Headers", "Access-Control-Allow-Headers, Token")
// 可选,是否允许后续请求携带认证信息Cookir,该值只能是true,不需要则不设置
resp.Header().Set("Access-Control-Allow-Credentials", "true")
2.简单请求
简单请求只有:
- 只可能有GET + POST +HEAD三种请求类型。而且必须满足下面的2和3两个特征,(POST请求也不全是简单请求,后面有例子)
- 不能有自定义头字段 ,只能有以下几种:
- Accept
- Accept-Language
- Content-Type
- Content-Language
- Content-Type的值只能是以下三种:
- text/plain
- multipart/form-data 上传文件用的
- application/x-www-form-urlencoded 表单提交的数据类型,一般的post请求的数据类型
除此之外都是复杂请求。
3.非简单请求过程分析(复杂请求):
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是 PUT
或 DELETE
,或者 Content-Type
字段的类型是 application/json
。
非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight),预检请求其实就是我们常说的 OPTIONS
请求,表示这个请求是用来询问的。头信息里面,关键字段 Origin
,表示请求来自哪个源,除 Origin
字段,"预检"请求的头信息包括两个特殊字段:
//该字段是必须的,用来列出浏览器的CORS请求会用到哪些HTTP方法
Access-Control-Request-Method
//该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段.
Access-Control-Request-Headers
相应的响应体可以有以下几段:
//该字段是必须的。它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求
Access-Control-Allow-Origin
//该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。
Access-Control-Allow-Methods
//如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。
Access-Control-Allow-Headers
//该字段可选。CORS请求时,XMLHttpRequest对象的response只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。
Access-Control-Expose-Headers
//该字段可选。它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。这个值也只能设为 true,如果服务器不要浏览器发送Cookie,删除该字段即可。
Access-Control-Allow-Credentials
//该字段可选,用来指定本次预检请求的有效期,单位为秒,在此期间,不用发出另一条预检请求。
Access-Control-Max-Age
①:复杂请求会先发送一个预检请求,方法为OPTIONS,只包含头部信息,不包含请求体,单独一个包发给服务器。包含如下字段:
Origin: 发送请求的页面的源
Access-Control-Request-Methods: 请求希望使用的方法
Access-Control-Request-Headers: (可选)要使用的逗号分隔的自定义头部列表
例如:Origin: http://aiecp.com
Access-Control-Request-Methods: POST
Access-Control-Request-Headers: content-type
②:服务器拿到之后,可以确定是否允许这种类型的请求,然后在响应中发送如下头部信息(不发送响应体,一定一定不要发送响应体):
Access-Control-Allow-Origin: http://aiecp.com
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, OPTIONS
Access-Control-Allow-Headers: content-type
Access-Control-Max-Age: 172800 // 这是缓存预检请求的秒数
③:只有预检请求请求完毕后且状态码为200才会发动正式的请求(预检请求没有请求体,相应的响应也没有响应体)
三、状态码设置为200依旧出错
浏览器不是傻子,你在后端直接返回200状态码,但是浏览器还会对你返回的数据进行一下检查,如果检查出你的响应头不符合规范
或者说你的响应体里面有内容,就会停止发送正式的请求(响应体有内容可能是错误信息)