Wails v3 集成 Gin 实现自定义路由:Gin-Routing 示例深度解析
【免费下载链接】wailsCreate beautiful applications using Go项目地址: https://gitcode.com/gh_mirrors/wa/wails
本指南以仓库中 gin-routing 示例 为骨架,完整讲解如何在 Wails v3 应用中把 Gin 作为核心路由引擎与资产处理器(Asset Handler),并通过中间件在 Wails 内建处理与 Gin 路由之间灵活分流。读完本文,你将掌握 Gin 路由挂载、Wails 中间件分流机制、/wails运行时路由保护、Go 与前端事件双向通信的完整实战方案。
示例概览:为什么要用 Gin 做路由
Wails v3 默认使用内置的 AssetServer 提供静态资源,并通过/wails/runtime.js暴露运行时 API。但在复杂业务中,你可能需要:
- 在桌面应用内部实现 RESTful API(如
/api/hello); - 让 WebView 加载的页面路由由 Go 侧统一控制;
- 复用 Gin 生态中成熟的中间件(日志、鉴权、CORS、限流等)。
v3/examples/gin-routing示例正是为此设计的。它演示了 5 个核心能力:
- 将 Gin 引擎挂载为 Wails 应用的资产处理器(Asset Handler);
- 编写 Wails 中间件,把请求在 Wails 与 Gin 之间按路径分发;
- 用 Gin 定义 API 端点并返回 JSON;
- 让 Gin 托管的前端页面与 Wails 后端通过事件机制通信;
- 自定义 Gin 中间件(请求日志)。
快速运行示例
进入示例目录,拉取依赖后直接运行:
cd v3/examples/gin-routing go mod tidy go run .启动后会出现一个 900×700 的窗口(标题为 "Wails + Gin Example"),其首页即由 Gin 的/路由渲染。go.mod中通过replace github.com/wailsapp/wails/v3 => ../..将 Wails v3 指向仓库本地的 v3 源码,便于随主仓库同步调试。
架构拆解:请求如何在 Wails 与 Gin 之间分流
整个示例的核心是Wails 层中间件(application.Middleware)+ Gin 层路由的两级体系,请求流程如下:
WebView 发起请求 │ ▼ Wails AssetServer 中间件链 │ ├─ 路径以 /wails 开头?──► 交给 Wails 内置处理器(runtime.js 等) │ └─ 其余路径 ──► 交给 Gin 引擎 │ ├─ /static/* ──► 内嵌静态文件 ├─ / ──► 首页 HTML └─ /api/hello ──► JSON APIWails 侧分流中间件:GinMiddleware
源码位于 main.go:
// GinMiddleware creates a middleware that passes requests to Gin if they're not handled by Wails func GinMiddleware(ginEngine *gin.Engine) application.Middleware { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Let Wails handle the `/wails` route if strings.HasPrefix(r.URL.Path, "/wails") { next.ServeHTTP(w, r) return } // Let Gin handle everything else ginEngine.ServeHTTP(w, r) }) } }关键设计点:
/wails前缀必须放行:前端页面通过import * as wails from '/wails/runtime.js'加载运行时,若将该请求也交给 Gin,运行时将不可用,事件通信和 Go 调用会全部失效;next.ServeHTTP触发 Wails 内建链路:在 application.go 中可以看到 Wails 自身拦截了/wails/runtime.js、/wails/transport.js等内置路径并提供实现,这些正是示例中间件放行的对象;- 其余请求全部转交 Gin:
ginEngine.ServeHTTP(w, r)直接复用标准http.Handler接口,因此中间件实现极度简洁,且与 Wails 的中间件类型天然兼容。
Wails 中间件类型与链式组合
从 application_options.go 可以看出,application.Middleware的本质是一个标准 HTTP 包装函数:
type Middleware func(next http.Handler) http.HandlerWails 还提供了ChainMiddleware(middleware ...Middleware) Middleware,允许把多个中间件反向链式组合成一个。这意味着你可以在GinMiddleware之外再叠加鉴权、日志、流量控制等任意标准 HTTP 中间件,而不必修改 Gin 本身。
资产处理器配置:AssetOptions
在创建应用时,示例把 Gin 引擎同时设置为Handler与Middleware(main.go):
app := application.New(application.Options{ Name: "Gin Example", Description: "A demo of using Gin with Wails", Mac: application.MacOptions{ ApplicationShouldTerminateAfterLastWindowClosed: true, }, Assets: application.AssetOptions{ Handler: ginEngine, Middleware: GinMiddleware(ginEngine), }, })对照 application_options.go 的字段定义:
| 字段 | 作用 | 本示例取值 |
|---|---|---|
Handler http.Handler | 向 WebView 提供全部内容的处理器 | ginEngine |
Middleware Middleware | 挂钩到 AssetServer 请求链,可动态跳过默认处理器以支持自定义路由 | GinMiddleware(ginEngine) |
DisableLogging bool | 关闭 AssetServer 请求日志(默认记录每个请求) | 未设置 |
官方注释特别说明:该 Middleware 注入在 Wails 内部中间件之前,且接收到的next就是 AssetServer 的默认处理器——这正是示例中"放行/wails、其余交给 Gin"的机制来源。若未定义 Middleware,则执行默认请求链(即用Handler提供资源)。
Gin 侧路由与中间件
使用 gin.New() 而非 gin.Default()
ginEngine := gin.New() // Using New() instead of Default() to add our own middleware ginEngine.Use(gin.Recovery()) ginEngine.Use(LoggingMiddleware())gin.Default()会自动挂载 Logger 与 Recovery,而这里为了演示自定义日志中间件,改用gin.New()后手动挂载:
gin.Recovery():兜底 panic 恢复,防止单个请求崩溃拖垮整个应用;LoggingMiddleware():示例自定义中间件,见 main.go。
自定义日志中间件
func LoggingMiddleware() gin.HandlerFunc { return func(c *gin.Context) { // Start timer startTime := time.Now() // Process request c.Next() // Calculate latency latency := time.Since(startTime) // Log request details log.Printf("[GIN] %s | %s | %s | %d | %s", c.Request.Method, c.Request.URL.Path, c.ClientIP(), c.Writer.Status(), latency, ) } }它演示了 Gin 中间件的标准写法:c.Next()之前记录起始时间,之后计算耗时,并输出方法、路径、客户端 IP、状态码与延迟,产出一条带[GIN]前缀的结构化日志。这套模式可以平滑扩展为鉴权(在c.Next()前校验并c.Abort())等业务中间件。
静态文件与 API 端点
// Serve embedded static files ginEngine.StaticFS("/static", http.FS(staticFiles)) // Define routes ginEngine.GET("/", func(c *gin.Context) { file, err := staticFiles.ReadFile("static/index.html") if err != nil { c.String(http.StatusInternalServerError, "Error reading index.html") return } c.Data(http.StatusOK, "text/html; charset=utf-8", file) }) ginEngine.GET("/api/hello", func(c *gin.Context) { c.JSON(http.StatusOK, gin.H{ "message": "Hello from Gin API!", "time": time.Now().Format(time.RFC3339), }) })- 前端资源通过
//go:embed static编译进二进制(main.go),/static由StaticFS提供,无需外部文件; /路由从内嵌 FS 中读取并渲染首页;/api/hello返回 JSON(消息 + RFC3339 时间戳),作为前端fetch调用的演示端点。
前端与 Wails 后端的双向通信
页面通过 Gin 提供,运行时仍来自 Wails
static/index.html 虽然由 Gin 渲染,但运行时模块依然从 Wails 加载:
<script type="module"> import * as wails from '/wails/runtime.js'; ...由于分流中间件放行了/wails前缀,该 import 能命中 Wails 的内置运行时实现,进而使用wails.Events.Emit向 Go 侧发事件。
前端发事件 → Go 侧监听
点击 "Trigger Event" 按钮会执行:
await wails.Events.Emit('gin-button-clicked', 'Hello from Gin!');Go 侧在 main.go 注册同名监听器:
app.Event.On("gin-button-clicked", func(event *application.CustomEvent) { log.Printf("Received event from frontend: %v", event.Data) })CustomEvent定义于 events.go,包含Name(事件名)、Data(任意载荷)、Sender(发送方窗口,应用级发送时为空)等字段,并支持Cancel()/IsCancelled()进行事件级取消控制。这意味着你完全可以把任意 JSON 数据(表单、操作指令、文件内容)从前端透传到 Go 侧。
前端调 API → Gin 返回 JSON
点击 "Call API" 按钮会fetch('/api/hello')并渲染返回的 JSON——这条请求链完全发生在 HTTP 层:WebView → Wails 分流中间件 → Gin/api/hello→ JSON 响应,无需任何桥接代码,充分体现了"API 即 HTTP"的简洁性。
窗口创建与应用生命周期
app.Window.NewWithOptions(application.WebviewWindowOptions{ Title: "Wails + Gin Example", Width: 900, Height: 700, URL: "/", }) err := app.Run() if err != nil { log.Fatal(err) }URL: "/"让窗口首屏直接命中 Gin 的/路由;app.Run()启动事件循环,Mac.ApplicationShouldTerminateAfterLastWindowClosed保证 macOS 下关闭窗口即退出进程。
扩展思路:把该模式移植到自己的项目
从源码结构看,这套方案具备很强的可移植性,可按以下步骤落地:
- 将
GinMiddleware复制到自己的项目,保留/wails前缀放行逻辑; - 在
AssetOptions中设置Handler: ginEngine, Middleware: GinMiddleware(ginEngine); - 用
ginEngine.StaticFS或Static挂载前端构建产物(如dist/); - 按需补充业务路由(
/api/*)与 Gin 中间件(鉴权、CORS、限流); - 前端继续从
/wails/runtime.js引入运行时,保持与 Wails 的桥接能力。
需要同时接入多个 HTTP 层时,可结合ChainMiddleware将 Wails 中间件串行组合;若希望 WebSocket 等高级能力也由 Gin 接管,可在中间件内按路径做更细粒度的分发。
小结
gin-routing示例给出了一个干净、可复用的"Wails 前端 + Gin 后端路由"集成范式:Wails 负责窗口与运行时桥接,Gin 负责页面与 API 路由,二者通过一个 10 行左右的application.Middleware完成无缝分流。对需要复杂路由、REST API 或复用 Gin 生态中间件的 Wails v3 应用而言,这是开箱即用的参考实现。
【免费下载链接】wailsCreate beautiful applications using Go项目地址: https://gitcode.com/gh_mirrors/wa/wails
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考