GB/T28181 全栈开发日记[2]:搭建服务端,解决跨域,接口联调

362 阅读5分钟

GB/T28181 全栈开发日记[2]:搭建服务端,解决跨域,接口联调

介绍

GoWVP (Golang Web Video Platfrom) 是一个 Go 语言实现的,基于 GB28181-2022 标准实现的网络视频平台,负责实现核心信令与设备管理后台部分,支持海康、大华、宇视等品牌的IPC、NVR、DVR接入。支持国标级联,支持rtsp/rtmp等视频流转发到国标平台,支持rtsp/rtmp等推流转发到国标平台。

技术栈

Golang v1.23, Goweb v1.x, Gin v1.10, Gorm v1.25 ...

React 19, Vite 6.x, Typescript, React-Router v7, React-Query v5, shadcn/ui ...

第二篇 搭建服务端

打包前端

在开始之前,我们将前端剩余一些工作完成。

此项目前后端合并部署,由 Go 服务端提供静态资源访问,实际部署无所谓,前端静态资源也可以用 nginx 部署,只是本文将按照前后端合并的方式部署。

前端打包后的静态文件,使用公共前缀路由能够很好约束并明确,此时请求的内容是静态资源还是接口。

例如以 /web 开头的路由前缀,均为前端静态资源。

编辑 vite.config.ts 文件

增加 base: mode === "development" ? "/" : "/web/",表示开发模式还是根目录,生产模式以 /web/ 为公共前缀。

vite 官方文档 有提到可以使用 ./ 设置相对基础路径,实际在开发模式中使用 base: "./",会出现路径访问不到资源的情况。

image-20250106221822159

编辑 react-router.config.ts 文件

增加 basename: import.meta.env.MODE === "development" ? "/" : "/web/",

image-20250106222504027

在终端执行 yarn build 即可打包成功,可在 build/client 目录中查看,我们将 client 重命名为 www,放到服务端项目目录下。

搭建服务端

Golang 服务端采用 GoWeb 模板,GoWeb 是一个专注于 REST API 的完整 CURD 解决方案,支持代码生成,整洁架构。

打开 goweb 仓库,选择使用此模板,创建自己的项目仓库后,克隆到本地。

image-20250106225627393

运行项目

配置目录 configs 在项目根目录,main 函数入口在 cmd/server ,编辑器默认运行会在 main.go 同级创建可执行文件,会导致读取不到配置,所以我们调整一下编辑器。

vscode 配置 launch.json,让可执行程序生成在项目根目录,运行 f5,

{
  // 使用 IntelliSense 了解相关属性。
  // 悬停以查看现有属性的描述。
  // 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
  "version": "0.2.0",
  "configurations": [
    {
      "name": "Launch Package",
      "type": "go",
      "request": "launch",
      "mode": "auto",
      "program": "${workspaceFolder}/cmd/server",
      "cwd": "${workspaceFolder}",
      "output": "__debug_${workspaceFolderBasename}",
    }
  ]
}

项目运行后,在终端执行 curl -s localhost:8080/health | jq ,可以看到如下图所示。/health 是 goweb 默认提供的健康检查接口,在 web/api/api.go 中定义。

windows 电脑可以用 git-bash 终端,我觉得记忆两套操作系统的命令还挺麻烦的,不如都用 linux 命令操作。无法操作终端时,浏览器访问是一样的效果哈。

image-20250106230641604

接入前端

我们以 /web 为网页静态资源前缀,使用 gin 框架,定义后台静态资源组,使用 gzip.Gzip 压缩加快前端资源下载速度。

在上面打包前端资源后,已将 www 移动到项目根目录下,使用 Static 提供对 www 资源的访问,增加 / 重定向到静态资源。

最后,前端打包使用的是浏览器路由,而非 hash 路由(/#/),此路由 Golang 服务端是不知道的,所以将 /web 前缀全部提供访问到 index.html,由静态资源处理这些前端路由。

代码如下:

const staticPrefix = "/web"
  const staticDir = "www"
  admin := r.Group(staticPrefix, gzip.Gzip(gzip.DefaultCompression))
  admin.Static("/", filepath.Join(system.Getwd(), staticDir))
  r.NoRoute(func(c *gin.Context) {
    // react-router 路由指向前端资源
    if strings.HasPrefix(c.Request.URL.Path, staticPrefix) {
      c.File(filepath.Join(system.Getwd(), staticDir, "index.html"))
      return
    }
    c.JSON(404, "来到了无人的荒漠")
  })
  // 访问根路径时重定向到前端资源
  r.GET("/", func(ctx *gin.Context) {
    ctx.Redirect(http.StatusPermanentRedirect, filepath.Join(staticPrefix, "index.html"))
  })

打开网页,访问 http://localhost:8080/,即可看到登录页面。

image-20250106232856634

前后端接口对接,向用户展示系统资源

gopsutil ****是一个 Go 语言的开源库,主要用于系统和进程监控。它为开发人员提供了方便的接口,用于获取系统级别的各种信息,如 CPU、内存、磁盘、网络、进程等方面的信息,并且跨平台支持 Linux、Windows、macOS、FreeBSD 等操作系统。

我们使用 gopsutil 库获取 Disk/CPU/Memory/Network 数据。

定义一个循环队列,定时通过 gopsutil 获取服务主机数据,写入循环队列,提供接口给前端访问。(略)

解决跨域

回到前端项目,我们使用 axios 向服务端发出请求。

axios 是一个基于 Promise 的 HTTP 客户端库,主要用于在浏览器和 Node.js 环境中进行 HTTP 请求。

在终端执行命令:

yarn add axios

image-20250106234240969

app/service/http.ts 定义请求封装。

const headers = {
  "Content-Type": "application/json",
};
​
export const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 60000,
  headers: headers,
  responseType: "json",
});

其中的import.meta.env.VITE_API_BASE_URL 是环境变量,可以在项目目录下创建 2 个文件

  • .env.development 开发模式环境变量,写入 VITE_API_BASE_URL=/api
  • .env.production 生产模式环境变量

在没有更多配置的情况下,直接请求会导致跨域资源无法访问,在 vite.config.ts 中定义如下内容,由 vite 做反向代理,替我们访问资源。

 server: {
    proxy: {
      "/api": {
        target: "http://localhost:8080",
        changeOrigin: true,
        rewrite: (path) => path.replace(/^/api/, ""),
      },
    },
  },
接口联调

app/service/model/stat.ts 定义 HTTP 接口响应模型,使用 postman/apifox/apipost 之类的工具,定义接口文档后,可以快速生成相关代码。

app/service/api/stat.ts 定义接口请求。

export async function FindStats() {
  return await GET<FindStatResponse>(`/stats`);
}

在任意页面,使用 react-query 每 5 秒请求一次服务端,获取最新服务主机资源状态。

 const query = useQuery({
    queryKey: ["dashboard"],
    queryFn: FindStats,
    refetchInterval: 5000,
    throwOnError: (error, query) => {
      return false;
    },
  });

打开相关页面,F12 查看 网络,可以看到每 5 秒获取一次数据。

image-20250106235728499

参考

axios 官方文档

goweb 官方仓库

gopsutil 官方仓库

gin 官方文档,快速入门

vite 官方文档,自定义代理规则

react query 官方文档,useQuery 使用说明