【计网】认识跨域,及其在go中通过注册CORS中间件解决跨域方案,go-zero、gin

一、跨域(CORS)是什么?

跨域,指的是浏览器出于安全限制,前端页面在访问不同源(协议、域名、端口任一不同)的后端接口时,会被浏览器拦截。
比如:

前端地址后端接口地址是否跨域
http://a.comhttp://a.com/api
http://a.comhttps://a.com/api是(协议不同)
http://a.comhttp://b.com/api是(域名不同)
http://a.com:8080http://a.com:8090/api是(端口不同)

浏览器会进行CORS检查。如果后端没有正确返回Access-Control-Allow-*的HTTP头信息,浏览器就会阻止前端访问。


二、浏览器的跨域流程

  1. 简单请求(Simple Request)

    • 请求方法限定:GET、POST、HEAD
    • Content-Type仅限于:application/x-www-form-urlencoded、multipart/form-data或text/plain
    • 没有对头部进行修改(如果修改,则是预检请求
  2. 预检请求(Preflight Request)

    • GET、POST、HEAD的请求方法
    • Content-Type为application/json等非简单值
    • 包含自定义头部字段

三、后端怎么处理跨域?

  • 常见跨域解决方案:CORS(跨源资源共享):W3C标准,服务端设置响应头实现

    • 处理跨域,就是要在HTTP响应中加上几个头:
  • Access-Control-Allow-Origin:允许访问的源(如*或https://example.com)

  • Access-Control-Allow-Methods:允许的HTTP方法(如GET, POST等)

  • Access-Control-Allow-Headers:允许的请求头字段

  • Access-Control-Allow-Credentials:是否允许发送Cookie(true/false)

  • Access-Control-Max-Age:预检请求缓存时间(秒)

CORS请求流程:

  1. 浏览器检测到跨域请求,自动添加Origin头
  2. 服务器检查Origin,决定是否允许并设置CORS响应头
  3. 对于预检请求,先发送OPTIONS请求确认权限
  4. 浏览器根据响应头决定是否允许实际请求

四、在 Go 后端怎么处理跨域?

这里讲两个框架:

1. 在 gin 框架中处理跨域

方法一:自己写中间件(不推荐)
func CorsMiddleware() gin.HandlerFunc {return func(c *gin.Context) {c.Writer.Header().Set("Access-Control-Allow-Origin", "*") // 允许所有域c.Writer.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")c.Writer.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")c.Writer.Header().Set("Access-Control-Allow-Credentials", "true")// 处理预检请求if c.Request.Method == "OPTIONS" {c.AbortWithStatus(204)return}c.Next()}
}

然后在main.go注册:

r := gin.Default()
r.Use(CorsMiddleware())

方法二:使用第三方库(推荐)

参考go文档:https://pkg.go.dev/github.com/gin-contrib/cors

用成熟的库,比如github.com/gin-contrib/cors

go get github.com/gin-contrib/cors

代码:

import "github.com/gin-contrib/cors"// Cors 创建并返回一个gin.HandlerFunc处理程序,用于配置跨域资源共享(CORS)。
// 该函数通过调用cors.New并传递一个cors.Config结构体来实现,该结构体包含了CORS的配置信息。
// 主要目的是为了允许来自任何源的请求(AllowAllOrigins: true),并指定允许的HTTP请求头和方法,
// 同时支持凭证的跨域请求(AllowCredentials: true)。
func Cors() gin.HandlerFunc {return cors.New(cors.Config{// AllowAllOrigins: true 表示允许来自所有域的请求。AllowAllOrigins: true,// AllowHeaders 指定了允许的请求头,包括Origin, Content-Length, Content-Type和Authorization。AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization",},// AllowMethods 指定了允许的HTTP方法,包括GET, POST, PUT, DELETE, HEAD和OPTIONS。AllowMethods: []string{"GET", "POST", "PUT", "DELETE", "HEAD", "OPTIONS",},// ExposeHeaders 指定了允许被访问的响应头,包括Content-Length, Access-Control-Allow-Origin,// Access-Control-Allow-Headers, Cache-Control, Content-Language和Content-Type。ExposeHeaders: []string{"Content-Length", "Access-Control-Allow-Origin", "Access-Control-Allow-Headers", "Cache-Control", "Content-Language", "Content-Type",},// AllowCredentials: true 表示允许跨域请求带上用户凭证。cookiesAllowCredentials: true,})
}

main.go

r := gin.Default()
r.Use(middleware.Cors(), middleware.Auth()) // 这里可以抽象到routers.go 不过要注意中间件注册一定要在路由分组之前,因为这里的注册操作相当于给所有的URL设定cors规则,前后顺序不能颠倒

r.Use()是给之后所有注册的路由挂载这个中间件。
也就是说,如果你先注册了路由,再r.Use(),那些已经注册了的路由就不会受到新的中间件的影响。
简单讲:中间件只保护在它后面注册的路由。


2. 在 go-zero 框架中处理跨域

go-zero默认就带跨域处理,只要你在服务配置里面加上跨域设置。

比如etc/your-api.yaml配置文件中,写:

Name: your-api
Host: 0.0.0.0
Port: 8888
Cors: true

注意这个Cors: true,加了以后,go-zero框架自动帮你处理CORS,包括预检请求(OPTIONS)!

如果要自定义,比如允许指定源,可以在自定义中间件里处理。

也可以自己写中间件(不推荐,go-zero已经做了)
func CorsMiddleware() func(http.Handler) http.Handler {return func(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {w.Header().Set("Access-Control-Allow-Origin", "*")w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")if r.Method == "OPTIONS" {w.WriteHeader(http.StatusNoContent)return}next.ServeHTTP(w, r)})}
}

注册:

server := rest.MustNewServer(c, rest.WithMiddlewares([]rest.Middleware{CorsMiddleware(),}...,
))

不过一般没必要,直接开Cors: true就行。


五、常见问题

  • OPTIONS请求后端返回404
    ➔ 需要后端显式处理OPTIONS方法,比如返回204 No Content。
  • 跨域时前端带了cookie但失败
    ➔ 需要Access-Control-Allow-Credentials: true,且Access-Control-Allow-Origin不能是*,必须指定具体域名。
  • 前端提示No ‘Access-Control-Allow-Origin’ header is present
    ➔ 后端没返回CORS头,检查中间件是否正确加载。

六、总结

  • 跨域是浏览器安全机制;
  • 处理跨域主要是设置HTTP响应头;
  • gin自己可以写中间件或用gin-contrib/cors
  • go-zero直接在配置里加Cors: true
  • OPTIONS预检请求一定要处理,否则请求直接失败。

七、针对go-zero中使用跨域使用方案拓展

  1. main.go方案
server := rest.MustNewServer(c.RestConf,rest.WithCustomCors(func(header http.Header) { // 1.这是自定义跨域逻辑var allowOrigin = "Access-Control-Allow-Origin"var allOrigins = "http://localhost:5173"var allowMethods = "Access-Control-Allow-Methods"var allowHeaders = "Access-Control-Allow-Headers"var exposeHeaders = "Access-Control-Expose-Headers"var methods = "GET, HEAD, POST, PATCH, PUT, DELETE, OPTIONS"var allowHeadersVal = "xxxx, Content-Type, Origin, X-CSRF-Token, Authorization, AccessToken, Token, Range"var exposeHeadersVal = "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers"var maxAgeHeader = "Access-Control-Max-Age"var maxAgeHeaderVal = "86400"header.Set(allowOrigin, allOrigins)header.Set(allowMethods, methods)header.Set(allowHeaders, allowHeadersVal)header.Set(exposeHeaders, exposeHeadersVal)header.Set(maxAgeHeader, maxAgeHeaderVal)}, func(w http.ResponseWriter) {}),)defer server.Stop()// 2.简单跨域//server := rest.MustNewServer(c.RestConf,//	rest.WithCors("http://localhost:5173"))// 3.自定义头 (预检请求)//server := rest.MustNewServer(c.RestConf,//	//顺序不能颠倒//	rest.WithCors("http://localhost:5173"),//	rest.WithCorsHeaders("xxxx"),//)

  1. 在 internal/middleware 目录下创建 cors.go 文件
package middlewareimport ("net/http"
)// Cors 中间件处理跨域请求
func Cors(next http.Handler) http.Handler {return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {// 1. 设置允许访问的源// 生产环境应替换为具体的域名,如 "https://example.com"w.Header().Set("Access-Control-Allow-Origin", "*")// 2. 设置允许的HTTP方法w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")// 3. 设置允许的请求头w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")// 4. 设置是否允许发送Cookie等凭证信息// 如果设置为true,则Access-Control-Allow-Origin不能为*w.Header().Set("Access-Control-Allow-Credentials", "false")// 5. 设置预检请求缓存时间(秒)w.Header().Set("Access-Control-Max-Age", "86400")// 6. 如果是OPTIONS方法(预检请求),直接返回204if r.Method == "OPTIONS" {w.WriteHeader(http.StatusNoContent)return}// 7. 继续处理后续中间件和路由next.ServeHTTP(w, r)})
}

在 main 函数中使用中间件

package mainimport ("flag""fmt""xxx/internal/config""xxx/internal/handler""xxx/internal/middleware" // 引入中间件包"xxx/internal/svc""github.com/zeromicro/go-zero/core/conf""github.com/zeromicro/go-zero/rest"
)var configFile = flag.String("f", "etc/xxx-api.yaml", "the config file")func main() {flag.Parse()// 1. 加载配置文件var c config.Configconf.MustLoad(*configFile, &c)// 2. 创建服务上下文ctx := svc.NewServiceContext(c)// 3. 创建服务实例server := rest.MustNewServer(c.RestConf)defer server.Stop()// 4. 注册CORS中间件(关键步骤)server.Use(middleware.Cors)// 5. 注册路由处理器handler.RegisterHandlers(server, ctx)// 6. 打印启动信息并启动服务fmt.Printf("Starting server at %s:%d...\n", c.Host, c.Port)server.Start()
}

https://github.com/0voice

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/79336.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内存性能测试方法

写于 2022 年 6 月 24 日 内存性能测试方法 - Wesley’s Blog dd方法测试 cat proc/meminfo console:/ # cat proc/meminfo MemTotal: 3858576 kB MemFree: 675328 kB MemAvailable: 1142452 kB Buffers: 65280 kB Cached: 992252 …

AVFormatContext 再分析二

说明 :将 avfromatContext 的变量依次打印分析,根据ffmpeg 给的说明,猜测,结合网上的文章字节写测试代码分析二。 37 AVInputFormat *iformat; /** * The input container format. * * Demuxing only, set by avfo…

深入了解Linux系统—— 进程优先级

前言 我们现在了解了进程是什么,进程状态表示什么 ,我们现在继续来了解进程的属性 —— 进程优先级 进程执行者 在了解进程优先级之前,先来思考一个问题:在我们进行文件访问操作时,操作系统是如何直到我们是谁&#x…

Expected SARSA算法详解:python 从零实现

🧠 向所有学习者致敬! “学习不是装满一桶水,而是点燃一把火。” —— 叶芝 我的博客主页: https://lizheng.blog.csdn.net 🌐 欢迎点击加入AI人工智能社区! 🚀 让我们一起努力,共创…

1penl配置

好的,根据您提供的 1pctl 命令输出信息,我们来重新依次回答您的所有问题: 第一:1Panel 怎么设置 IP 地址? 根据您提供的 user-info 输出: 面板地址: http://$LOCAL_IP:34523/93d8d2d705 这里的 $LOCAL_I…

链表的回文结构题解

首先阅读题目: 1.要保证是回文结构 2.他的时间复杂度为O(n)、空间复杂度为O(1) 给出思路: 1.首先利用一个函数找到中间节点 2.利用一个函数逆置中间节点往后的所有节点 3.现在有两个链表,第一个链表取头节点一直到中间节点、第二个链表取头结点到尾…

【LLaMA-Factory实战】1.3命令行深度操作:YAML配置与多GPU训练全解析

一、引言 在大模型微调场景中,命令行操作是实现自动化、规模化训练的核心手段。LLaMA-Factory通过YAML配置文件和多GPU分布式训练技术,支持开发者高效管理复杂训练参数,突破单机算力限制。本文将结合结构图、实战代码和生产级部署经验&#…

C++负载均衡远程调用学习之 Dns-Route关系构建

目录 1.LARS-DNS-MYSQL环境搭建 2.LARSDNS-系统整体模块的简单说明 3.Lars-Dns-功能说明 4.Lars-Dns-数据表的创建 5.Lars-Dns-整体功能说明 6.Lars-DnsV0.1-Route类的单例实现 7.Lars-DnsV0.1-Route类的链接数据库方法实现 8.Lars-DnsV0.1-定义存放RouteData关系的map数…

fastapi+vue中的用户权限管理设计

数据库设计:RBAC数据模型 这是一个典型的基于SQLAlchemy的RBAC权限系统数据模型实现,各模型分工明确,共同构成完整的权限管理系统。 图解说明: 实体关系: 用户(USER)和角色(ROLE)通过 USER_ROLE 中间表实现多对多关系…

【Python实战】飞机大战

开发一个飞机大战游戏是Python学习的经典实战项目,尤其适合结合面向对象编程和游戏框架(如Pygame)进行实践。以下是游戏设计的核心考虑因素和模块划分建议: 一、游戏设计核心考虑因素 性能优化 Python游戏需注意帧率控制&#xff…

Flowable7.x学习笔记(十八)拾取我的待办

前言 本文从解读源码到实现功能,完整的学习Flowable的【TaskService】-【claim】方法实现的任务拾取功能。 一、概述 当调用 TaskService.claim(taskId, userId) 时,Flowable 会先加载并校验任务实体,再判断该任务是否已被认领;若…

SQL经典实例

第1章 检索记录 1.1 检索所有行和列 知识点:使用SELECT *快速检索表中所有列;显式列出列名(如SELECT col1, col2)提高可读性和可控性,尤其在编程场景中更清晰。 1.2 筛选行 知识点:通过WHERE子句过滤符合条…

HTTPcookie与session实现

1.HTTP Cookie 定义 HTTP Cookie (也称为 Web Cookie 、浏览器 Cookie 或简称 Cookie )是服务器发送到 用户浏览器并保存在浏览器上的一小块数据,它会在浏览器之后向同一服务器再次发 起请求时被携带并发送到服务器上。通常&#xff0…

【算法基础】冒泡排序算法 - JAVA

一、算法基础 1.1 什么是冒泡排序 冒泡排序是一种简单直观的比较排序算法。它重复地走访待排序的数列,依次比较相邻两个元素,如果顺序错误就交换它们,直到没有元素需要交换为止。 1.2 基本思想 比较相邻元素:从头开始&#xf…

0902Redux_状态管理-react-仿低代码平台项目

文章目录 1 Redux 概述1.1 核心概念1.2 基本组成1.3 工作流程1.4 中间件(Middleware)1.5 适用场景1.6 优缺点1.7 Redux Toolkit(现代推荐)1.8 与其他工具的对比1.9 总结 2 todoList 待办事项案例3 Redux开发者工具3.1 核心功能3.2…

《ATPL地面培训教材13:飞行原理》——第6章:阻力

翻译:Leweslyh;工具:Cursor & Claude 3.7;过程稿 第6章:阻力 目录 引言寄生阻力诱导阻力减少诱导阻力的方法升力对寄生阻力的影响飞机总阻力飞机总重量对总阻力的影响高度对总阻力的影响构型对总阻力的影响速度稳…

C++总结01-类型相关

一、数据存储 1.程序数据段 • 静态(全局)数据区:全局变量、静态变量 • 堆内存:程序员手动分配、手动释放 • 栈内存:编译器自动分配、自动释放 • 常量区:编译时大小、值确定不可修改 2.程序代码段 •…

【Hot 100】94. 二叉树的中序遍历

目录 引言二叉树的中序遍历我的解题代码优化更清晰的表述建议: 🙋‍♂️ 作者:海码007📜 专栏:算法专栏💥 标题:【Hot 100】94. 二叉树的中序遍历❣️ 寄语:书到用时方恨少&#xff…

大语言模型(LLMs)微调技术总结

文章目录 全面总结当前大语言模型(LLM)微调技术1. 引言2. 为什么需要微调?3. 微调技术分类概览4. 各种微调技术详细介绍4.1 基础微调方法4.1.1 有监督微调(Supervised Fine-Tuning, SFT)4.1.2 全参数微调(F…

解决Maven项目中报错“java不支持版本6即更高的版本 7”

错误背景 当Maven项目编译或运行时出现错误提示 Java不支持版本6即更高的版本7,通常是由于项目配置的JDK版本与当前环境或编译器设置不一致导致的。例如: 项目配置的Java版本为6或7,但实际使用的是JDK 17。Maven或IDE的编译器未正确指定目标…