Go:怎么和API服务器一起serve前端代码

Go:怎么和API服务器一起serve前端代码

1. 前言

前后端分离式是大家都认可的一种项目开发模式, 当时今天老周要在这里提供使用go语言一种前后端混合的开发模式. 这种前后端混合使用go语言开启一个服务占用一个端口,同时提供前端代码的输出和提供RESTful接口输出. 这种Go语言前后端混合模式的好处,

  • 部署简单: 因为把前端代码打包压缩转换成go代码的字符串常量, npm run build之后前端代码包含在编译之后的二进制文件中,部署的时候不需要拷贝前端代码到nginx前端目录. 这个app服务只需要一个二进制文件,从此告别部署拷贝前端代码和相对路径的疼苦.
  • 减少nginx依赖: npm run build之后前端代码,通过golang标准库http.FileServer输出到浏览器,收益不需要nginx开启一个server后者location单独提供前端代码服务, 因为减少了nginx这一层服务,和直接从内存中加载前端代码而不是从硬盘加载html/js/css,请求耗时减少2ms
  • 避免跨域OPTIONS请求: 因为前端代码和API服务是用一个服务使用相同的端口,从此从此告别讨厌并且耗时的OPTIONS请求(当然您也可以在通过nginx location 配置减少options跨域,这里就不详细介绍了),

Go语言是一门后端语言,

2. 使用

  1. git clone git clone https://github.com/libragen/felix.git
  2. go install cd felix && go install (设置GOBIN环境变量)
  3. felix ginbin -h 运行帮助命令
$ felix ginbin -h
Is this a crazy idea? No, not necessarily.
If you're building a tool that has a Web component,
you typically want to serve some images, CSS and JavaScript.
You like the comfort of distributing a single binary,
so you don't want to mess with deploying them elsewhere.
If your static files are not large in size and will be browsed by a few people,
ginbin is a solution you are looking for

Usage:
  felix ginbin [flags]

Flags:
  -c, --comment string   The package comment. An empty value disables this comment.
  -d, --dest string      The destination path of the generated package. (default ".")
  -f, --force            Overwrite destination file if it already exists. (default true)
  -h, --help             help for ginbin
  -m, --mtime            Ignore modification times on files.
  -p, --package string   The destination path of the generated package. (default "felixbin")
  -s, --src string       The path of the source directory. (default "dist")
  -t, --tags string      The golang tags.
  -z, --zip              Do not use compression to shrink the files.

2.1 使用实例

编译npm run build 自己的vuejs SPA前端项目,复制编译好的dist目录路径.

  • cd ${goWebApp项目目录}
  • felix ginbin -s ${前端编译好的dist目录路径} felix ginbin 重要参数说明: -s 前端编译好的代码目录,也可以适用于普通的前端项目 -d输出go代码的目录(包),默认当前工作目录,其他参数选填.

前端代码转换成go代码最终输出效果如图,staticbin 前端代码转换成go代码最终输出效果如图

2.2 把前端代码加载到项目API中

先上代码再说原理 ssh2ws.go

package ssh2ws

import (
	"github.com/libragen/felix/staticbin"
	"github.com/gin-gonic/gin"
	"time"
)

func RunSsh2ws(bindAddress, user, password, secret string, expire time.Duration, verbose bool) error {
	//config jwt variables
	r := gin.New()
	//sever static file in http's root path
	binStaticMiddleware, err := staticbin.NewGinStaticBinMiddleware("/")
	if err != nil {
		return err
	}
	r.Use(binStaticMiddleware)

	api := r.Group("api")
	r.POST("api/login", internal.Login)
	r.POST("api/register", internal.UserCreate)

加载ginbin生产的前端代码package作为gin middleware

加载ginbin生产的前端代码package作为gin middleware binStaticMiddleware, err := staticbin.NewGinStaticBinMiddleware("/"),同时设置url-path:/ 映射前端代码

gin使用前端代码中间件

r.Use(binStaticMiddleware)

加载RESTful APIs Gin

	api := r.Group("api")
	r.POST("api/login", internal.Login)
	r.POST("api/register", internal.UserCreate)

最终效果见 http://home.mojotv.cn:2222/#/login

最终效果见 https://sshfortress.mojotv.cn/#/login

3. 原理

  1. 遍历前端代码目录
  2. 使用标准库archive/zip压缩文件到bytes.Buffer
  3. bytes.Buffer 转换输出go 常量字符串
  4. go const 字符串 转换成 File interface的结构体
  5. 编写gin middleware拦截请求,http.Serve 前端文件

源代码https://github.com/libragen/felix/blob/master/ginbro/ginstatic.go

4. 附录GOBIN

环境变量GOBIN表示我们开发程序编译后二进制命令的安装目录. 当我们使用go install命令编译和打包应用程序时,该命令会将编译后二进制程序打包GOBIN目录,一般我们将GOBIN设置为GOPATH/bin目录 Linux设置GOBIN演示 export GOBIN=$GOPATH/bin 复制代码上面的代码中,我们都是使用export命令设置环境变量的,这样设置只能在当前shell中有效,如果想一直有效,如在Linux中,则应该将环境变量添加到/etc/profile ~/.bashrc等文件当中.

我的另外一个项目也使用了这样的技术

4.1 github.com/mojocn/sshfortress.

目录