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 使用说明

### PyCharm 打开文件显示不全的解决方案 当遇到PyCharm打开文件显示不全的情况时,可以尝试以下几种方法来解决问题。 #### 方法一:清理缓存并重启IDE 有时IDE内部缓存可能导致文件加载异常。通过清除缓存再启动程序能够有效改善此状况。具体操作路径为`File -> Invalidate Caches / Restart...`,之后按照提示完成相应动作即可[^1]。 #### 方法二:调整编辑器字体设置 如果是因为字体原因造成的内容显示问题,则可以通过修改编辑区内的文字样式来进行修复。进入`Settings/Preferences | Editor | Font`选项卡内更改合适的字号大小以及启用抗锯齿功能等参数配置[^2]。 #### 方法三:检查项目结构配置 对于某些特定场景下的源码视图缺失现象,可能是由于当前工作空间未能正确识别全部模块所引起。此时应该核查Project Structure里的Content Roots设定项是否涵盖了整个工程根目录;必要时可手动添加遗漏部分,并保存变更生效[^3]。 ```python # 示例代码用于展示如何获取当前项目的根路径,在实际应用中可根据需求调用该函数辅助排查问题 import os def get_project_root(): current_file = os.path.abspath(__file__) project_dir = os.path.dirname(current_file) while not os.path.exists(os.path.join(project_dir, '.idea')): parent_dir = os.path.dirname(project_dir) if parent_dir == project_dir: break project_dir = parent_dir return project_dir print(f"Current Project Root Directory is {get_project_root()}") ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值