在前后端分离开发时跨域经常是一个不可回避的问题,这里介绍一个比较简单的办法,前端 vue.js,后端 go

首先要区分简单请求和复杂请求

这里阮一峰有详细讲解过一个: 跨域资源共享 CORS 详解

如果你有一种太长不看的想法,就听我给你简单说一下:

  • 简单路由: 指的就是你平时表单提交,ajax 提交,使用GET,POST,HEAD,http 头信息里面没有杂七杂八 的东西的请求 简单请求示例
  • 复杂请求: 就是除了简单请求之外的请求,比如你在 http 头里有自定义的字段,或者你使用像DELETE,PATCH这 样的方法 复杂请求示例

你会发现复杂请求最明显的变化就是浏览器每次去请求接口的时候会发送两次,这里第一次发送的必定是一个请求方法为OPTIONS 的,第二个才是你写的方法,我截图里的就是POST, 第二张截图之所以被判定为复杂请求就是因为我加了一个X-TOKEN这个 header,而如果第一次的OPTIONS方法的请求如果失败了也就不会发送第二条信息了,至于为什么,我只能说这是规定

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

对于简单请求和复杂请求的处理(后端)

既然叫简单请求处理起来也很简单,下面已经列出代码, 关键就是设置Access-Control-Allow这一些参数就行, 复杂请求就是添加一个针对OPTIONS的处理方式,通常情况返回一个空字符串就行至于跨域验证之类的就是八仙过海各显神通了 各家有个家的处理方法

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
)

type httpServer struct {
}

func (s *httpServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	// Stop here if its Preflighted OPTIONS request
	if origin := r.Header.Get("Origin"); origin != "" {
		w.Header().Set("Access-Control-Allow-Origin", "*")	// 限制请求方的域 * 就是不限制
		w.Header().Set("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE")	// 限制请求方式
		w.Header().Set("Access-Control-Allow-Headers","Action, Module")   //有使用自定义头 需要这个,Action, Module是例子
    w.Header().Set("Access-Control-Allow-Headers", "Content-Type,Authorization,X-Token") //允许的header的类型
	}

	// if r.Method == "OPTIONS" {	// 针对复杂请求时做出的处理
	// 	return
	// }

	w.Write([]byte("hello"))
}

func main() {
	addr := flag.String("http-address", "", "")
	flag.Parse()

	var h httpServer

	httpListener, err := net.Listen("tcp", *addr)
	server := http.Server{Handler: &h,}
	server.Serve(httpListener)

	fmt.Println("finish ", err)
}

补充一下后续踩过的坑

当设置 Access-Control-Allow-Credentials: true 时, Access-Control-Allow-Origin 不能是 *, 而是要写明域 这时就会出现一个问题: 我想要跨多个域呢? 那么就一定不能把Access-Control-Allow-Origin写死了, 需要根据请求里的host 来判定是否允许跨域, 需要检测你整个项目里是不是有写死 Access-Control-Allow-Origin: *的header 或者在nginx里面设置过