当前位置: 首页 > article >正文

GIN 反向代理功能

今天想试着做一个gin的反向代理,代理其他的服务,给前端使用,思路如下:

主要组成部分

  1. 跨域中间件(Cors 函数)

    • Cors 中间件函数通过在 HTTP 响应中设置合适的头部信息,允许跨域请求。
    • 它添加了允许的方法(例如 POSTGETOPTIONS 等),并向客户端公开特定的头部字段。
    • 对于预检请求(即 OPTIONS 请求),直接返回 “ok” 表示请求被允许。
    • 还包含一个 defer 函数,用于在发生 panic 时进行恢复处理。
  2. 反向代理中间件(ReverseProxy 函数)

    • ReverseProxy 函数将传入的请求转发到由 targetURL 指定的目标服务器。
    • 使用 httputil.NewSingleHostReverseProxy 为指定目标创建一个反向代理。
    • 调整请求的 host 和 URL,使其与目标服务器匹配,以确保请求被正确转发。
  3. 动态更新目标 URL(UpdateTargetURL 函数)

    • 该端点(/api/update-target)用于更新反向代理所使用的 targetURL,这是一个受读写锁保护的全局变量。
    • 接收包含 hostport 值的 JSON 负载,以设置新的目标 URL。
    • 更新后的 targetURL 会被格式化并存储,然后返回一个确认消息。
  4. 服务器启动函数(Start 函数)

    • Start 函数根据配置的模式(debug 或 release)初始化 Gin 服务器。
    • 全局应用 CORS 中间件,并设置一个用于更新目标 URL 的端点。
    • 设置反向代理路由,将 /v1/*proxyPath 下的所有请求转发到当前目标 URL。
    • 服务器监听并在端口 8080 上运行。

功能要求

  • 并发和安全性:通过使用 mu 锁,确保对 targetURL 的线程安全访问,因为多个 goroutine 可能同时读取或更新此值。
  • 错误处理UpdateTargetURL 函数处理格式不正确的 JSON 请求,如果输入的 JSON 无效,则返回 400 Bad Request
  • 可配置的代理目标:允许动态更新 targetURL 使该服务器设置更灵活,可以根据需要将请求路由到不同的后端服务器。
  • 调试:在 Start 中检查配置的详细模式,以根据需要设置 Gin 的 debug 或 release 模式,用于在生产与开发环境中切换日志记录。

此设置非常适合需要将请求动态代理到不同后端服务器的场景,同时处理跨域请求并确保并发访问的安全性。

代码实现
package web

import (
	"fmt"
	"net/http"
	"net/http/httputil"
	"net/url"
	"sync"

	"github.com/gin-gonic/gin"
	"client/backend/internal/config"
)

var (
	targetURL string       = "http://127.0.0.1:8080" // Default target URL
	mu        sync.RWMutex                              // Mutex for safe concurrent access
)

// 跨域中间件
func Cors() gin.HandlerFunc {
	return func(c *gin.Context) {
		method := c.Request.Method
		origin := c.Request.Header.Get("Origin") //请求头部
		if origin != "" {
			//接收客户端发送的origin (重要!)
			c.Writer.Header().Set("Access-Control-Allow-Origin", origin)
			//服务器支持的所有跨域请求的方法
			c.Header("Access-Control-Allow-Methods", "POST, GET, OPTIONS, PUT, DELETE,UPDATE")
			//允许跨域设置可以返回其他子段,可以自定义字段
			c.Header("Access-Control-Allow-Headers", "Content-Type,Authorization, Content-Length, X-CSRF-Token, Token,session")
			// 允许浏览器(客户端)可以解析的头部 (重要)
			c.Header("Access-Control-Expose-Headers", "Content-Length, Access-Control-Allow-Origin, Access-Control-Allow-Headers")
			//设置缓存时间
			c.Header("Access-Control-Max-Age", "172800")
			//允许客户端传递校验信息比如 cookie (重要)
			c.Header("Access-Control-Allow-Headers", "Origin, Content-Type, Accept, Authorization, X-Requested-With")
			c.Header("Access-Control-Allow-Credentials", "true")

		}

		//允许类型校验
		if method == "OPTIONS" {
			c.JSON(http.StatusOK, "ok!")
		}

		defer func() {
			if err := recover(); err != nil {
				fmt.Println("Panic info is: %v", err)
			}
		}()

		c.Next()
	}
}

// ReverseProxy function to forward requests to the target server.
// ReverseProxy function to forward requests to the current target server.
func ReverseProxy() gin.HandlerFunc {
	return func(c *gin.Context) {
		mu.RLock() // Lock for reading
		defer mu.RUnlock()

		target, err := url.Parse(targetURL)
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{"error": "Invalid target URL"})
			return
		}

		proxy := httputil.NewSingleHostReverseProxy(target)

		// Update the request to the target URL
		c.Request.Host = target.Host
		c.Request.URL.Host = target.Host
		c.Request.URL.Scheme = target.Scheme

		// Serve the request using the reverse proxy
		proxy.ServeHTTP(c.Writer, c.Request)
	}
}

type BackendServer struct {
	Host string `json:"host"`
	Port int    `json:"port"`
}

// UpdateTargetURL updates the target URL dynamically.
func UpdateTargetURL(c *gin.Context) {

	var options BackendServer

	// Bind the JSON body to the newTarget struct
	if err := c.ShouldBindJSON(&options); err != nil {
		c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request body", "code": 400})
		return
	}

	mu.Lock() // Lock for writing
	defer mu.Unlock()

	// Update the global targetURL variable
	targetURL = fmt.Sprintf("%s:%d", options.Host, options.Port)
	c.JSON(http.StatusOK, gin.H{"message": "Target URL updated", "new_target": targetURL, "code": 200})
}
func Start() {
	if config.C().Verbose {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	router := gin.Default()

	router.Use(Cors())
	// Route to update the target server URL
	router.POST("/api/update", UpdateTargetURL)
	// Proxy all requests to the /api endpoint to the current target server
	router.Any("/v1/*proxyPath", ReverseProxy())

	// Run the Gin server on port 8080
	if err := router.Run(":8080"); err != nil {
		panic(err)
	}
}


http://www.kler.cn/a/374825.html

相关文章:

  • 在MacOS玩RPG游戏 - RPGViewerPlus
  • 黑马软件测试第二篇_功能测试
  • metasploit/modules/payloads 有哪些模块,以及具体使用案例
  • JavaEE-多线程初阶(2)
  • 使用 Git 命令将本地项目上传到 GitLab
  • QTreeWidget使用记录(2)
  • C/C++ 每日一练:二叉树的先序遍历
  • Webserver(2.6)信号
  • 信号完整性SI总结【小登培训】
  • OpenCV基础知识
  • DB-GPT系列(二):DB-GPT部署(镜像一键部署、源码部署)
  • C++ 代码工程化
  • 微信小程序,打开新的项目,调试遇见[ app.json 文件内容错误] app.json: 在项目根目录未找到 app.json
  • 【行业应用-工业防爆】本安型工业防爆网关,实现安全高效的数据传输与监控
  • 如何解决docker镜像下载失败问题
  • nfs作业
  • Docker | 容器数据卷在docker中的角色:持久化存储
  • WebSocket简单使用
  • Python实现图像(边缘)锐化:梯度锐化、Roberts 算子、Laplace算子、Sobel算子的详细方法
  • 【PythonWeb开发】Flask-RESTful字段格式化
  • C语言常见进制 (二进制、八进制、十进制、十六进制)详解
  • uniapp使用echart
  • Litctf-web
  • Docker(二):Docker的基本使用
  • 从0攒一个分布大数据/人工智能/数据库 处理引擎
  • 躺平成长-下一个更新的数据(躺平成长数据显示核心)