在搭建Go语言的项目时,我们可能会习惯gin-gonic这样的框架。
这里来介绍一款目前简单易用,功能强大的开源框架 Bud。项目开源不到一年的时间,但是目前已经3.9K+ star,发展势头非常好。
Bud的地址:github.com/livebud/bud
Bud这个名字的由来是Buddy(伙伴)的缩写。作者希望好的框架就像安静的技术伙伴,可以提供很大的帮助。
创建一个Bud项目非常简单。
Node v14+ (官方会在v0.3版本的时候去除node js依赖)
// mac的话可以直接 brew install node
Go v1.17+
// 可以参考 Go语言安装和配置
首先执行这个命令。会远程拉一个脚本并执行 Bud 的安装
- curl -sf https://raw.githubusercontent.com/livebud/bud/main/install.sh | sh
- 复制代码
然后使用命令 bud -h 验证安装是否成功。
用 bud create 来创建一个项目,参数是指定的当前路径下的项目目录。
- # 在 blog/ 目录下创建一个新的bud项目
- bud create blog
- 复制代码
会生成这样的目录和文件
- $ ls
- go.mod node_modules/ package-lock.json package.json
- 复制代码
Bud的标准目录结构如下所示:
- $YOUR_APP
- ├─ bud
- ├─ controller
- ├─ internal
- ├─ public
- └─ view
- 复制代码
bud 目录包含框架生成的代码。偶尔会需要从这个目录导入生成的包,但在大多数情况下可以忽略它的内容。 建议将此目录置于源代码控制之外。
controller 目录用于放置响应请求的代码。可用于为视图获取动态数据或提供 API。
internal 目录用于放置项目自身的代码,bud框架永远不会用到这个目录。
public 目录用于放置静态资源,比如图片和视频。
view 目录用于存储 Svelte 文件以显示给您的访问者。视图通常与控制器配对。
运行bud run可以启动项目。
- # 在默认的 3000 端口启动 bud server
- bud run
-
- # 在指定端口启动 bud server
- bud run --port=8080
-
- # 以非热加载模式启动 bud server
- bud run --hot=false
- 复制代码
###1. 网络路由
后端模块的请求处理逻辑,都是从controller开始。
当我们需要创建controller的时候,可以直接通过命令的方式创建。
- # 创建一个叫作post的controller
- bud new controller post
-
- # 创建一个叫作post的controller,有index和show的function
- bud new controller post index show
-
- # 创建一个指定路由路径的controller
- bud new controller post:/
- 复制代码
Bud框架的controller目录结构示例如下所示:
- $YOUR_APP
- └─ controller
- ├─ controller.go -> root controller
- ├─ posts
- │ ├─ posts.go -> posts controller
- │ └─ comments
- │ └─ comments.go -> comments controller
- └─ users
- └─ users.go -> users controller
- 复制代码
users controller的示例如下,Bud框架对于controller的方法,都有对应的默认url路径。
- package users
-
- // Controller for /users
- type Controller struct {}
-
- // User type
- type User struct {
- ID int
- Name string
- Age int
- }
-
- // Index shows a list of users
- // GET /users
- func (c *Controller) Index() ([]*User, error) {}
-
- // New user page
- // GET /users/new
- func (c *Controller) New() {}
-
- // Create a new user
- // POST /users
- func (c *Controller) Create(name string, age int) (*User, error) {}
-
- // Show a user
- // GET /users/:id
- func (c *Controller) Show(id int) (*User, error) {}
-
- // Update a user
- // PATCH /users/:id
- func (c *Controller) Update(id int, name string, age int) error {}
-
- // Delete a user
- // DELETE /users/:id
- func (c *Controller) Delete(id int) error {}
-
- // Edit user page
- // GET /users/:id/edit
- func (c *Controller) Edit(id int) (*User, error) {}
- 复制代码
例如一个这样的请求
对于路径 /posts/:id/comments?order=xx&author=xx
- GET /posts/10/comments?order=asc&author=Alice
- 复制代码
那么实际的GET请求参数是这样的:
- {
- "id": 10,
- "order": "asc",
- "author": "Alice"
- }
- 复制代码
- POST /posts/10/comments?author=Alice
- {
- "email": "alice@livebud.com"
- }
- 复制代码
那么实际的POST请求参数是这样的:
- {
- "id": 10,
- "author": "Alice",
- "email": "alice@livebud.com"
- }
- 复制代码
对于path参数和query参数有冲突的情况,例如:
- POST /posts/10/comments?id=20&author=Alice
- {
- "id": 30,
- "author": "Bob"
- }
- 复制代码
那这时候,参数生效的优先级是这样的
1.路径值(最高优先级)
2.query参数
3.请求正文(最低优先级)
那么,最后生效的参数是这样的:
- {
- "id": 10,
- "author": "Bob"
- }
- 复制代码
bud框架对这一类的功能,已经有处理的逻辑了。并且对于错误参数的处理也已经有一套逻辑,不需要用户再去写这类逻辑。
bud使用的前端框架是Svelte.JS,当如1中所示创建controller和action之后,可以生成对应的svelte文件
- $YOUR_APP
- └─ view
- ├─ index.svelte -> Root index page
- ├─ posts
- │ ├─ edit.svelte -> Edit post page
- │ ├─ index.svelte -> Post index page
- │ ├─ new.svelte -> New post page
- │ ├─ show.svelte -> Show post page
- │ └─ Post.svelte -> Post component (not rendered)
- └─ users
- ├─ edit.svelte -> Edit user page
- ├─ index.svelte -> User index page
- └─ show.svelte -> Show user page
- 复制代码
例如可以这样让controller和view交互:
controller/users/users.go
- package users
-
- type Controller struct {}
-
- type User struct {
- Name string
- Age int
- }
-
- func (c *Controller) Index() []*User {
- return []*User{
- {"Matt", 32},
- {"Mia", 31},
- {"Mike", 26},
- }
- }
- 复制代码
view/users/index.svelte
- <script>
- export let users = []
- </script>
-
- <h1>Users</h1>
-
- <ul>
- {#each users as user}
- <li>{user.name} is {user.age} years old</li>
- {/each}
- </ul>
- 复制代码
插件以 bud- 前缀开头,看起来像任何其他 Bud 应用程序。例如,让我们创建一个插件来服务 Tailwind 的 preflight.css 文件
- bud-tailwind/
- ├── public
- │ └── preflight.css
- ├── go.mod
- └── go.sum
- 复制代码
如果Bud项目的目录如下所示
- $YOUR_APP
- ├── controller
- │ └── post.go
- ├── public
- │ └── favicon.ico
- ├── view
- │ ├── index.svelte
- │ └── show.svelte
- ├── go.mod
- └── go.sum
- 复制代码
那通过执行命令 go get
github.com/livebud/bud-tailwind之后,preflight.css就会安装到该bud项目中。
- $YOUR_APP
- ├── controller
- │ └── post.go
- ├── public
- │ ├── favicon.ico
- + │ └── preflight.css
- ├── view
- │ ├── index.svelte
- │ └── show.svelte
- ├── go.mod
- └── go.sum
- 复制代码
但是目前Bud框架并没有自己的ORM,用户需要自行选择orm框架,通过依赖注入在控制器中使用。
另外,还有全栈框架包含的特性,如错误处理、依赖注入、数据验证及安全、文件与存储等都可以在官方文档上找到。