快速开始
本章从零搭建一个可运行的 fun 服务:安装、写服务、启动、用 curl 调用、生成 TS 客户端、前端联调。
环境要求
| 工具 | 版本 | 说明 |
|---|---|---|
| Go | ≥ 1.26 | 框架模块声明 go 1.26.1 |
| Node.js | ≥ 18 | 仅前端 / 生成产物消费方需要 |
| fasthttp | v1.73.0 | 由 go get 自动带入 |
安装
bash
mkdir hello-fun && cd hello-fun
go mod init hello-fun
go get github.com/cyi-cc/fun@latest最小可运行示例
新建 main.go:
go
package main
import (
"log"
"github.com/cyi-cc/fun"
)
// UserSvc 用户服务:嵌入 fun.Ctx,导出方法即 RPC 端点
type UserSvc struct {
fun.Ctx
}
type GetUserDto struct {
Id int64 // 数值一律定宽整型
}
type UserDto struct {
Id int64
Name string
}
// Get 导出方法 → 端点 UserSvc.Get
func (s *UserSvc) Get(dto GetUserDto) (UserDto, error) {
return UserDto{Id: dto.Id, Name: "阿宝"}, nil
}
func main() {
f := fun.GetFun()
if err := f.BindService(&UserSvc{}); err != nil {
log.Fatal(err)
}
go f.Start(8080) // 监听 :8080,业务只响应 POST /cell
select {} // 阻塞主 goroutine
}运行:
bash
go run main.go用 curl 调用
所有业务请求都是 POST /cell,body 指定服务与方法:
bash
curl -X POST http://localhost:8080/cell \
-H 'Content-Type: application/json' \
-d '{"serviceName":"UserSvc","methodName":"get","data":{"Id":1}}'响应是统一的 Result,所有键递归转首字母小写:
json
{"status":0,"data":{"id":1,"name":"阿宝"}}字段名大小写不敏感:请求里写 "id":1 或 "Id":1 都能匹配到 GetUserDto.Id。
完整示例:仓库自带的 demo
example/demo 演示了全部核心特性——定宽整型与可空指针字段、枚举、业务错误码、两种流式签名:
go
// OrderStatus 订单状态枚举:uint8 底层 + Names/DisplayNames
type OrderStatus uint8
func (OrderStatus) Names() []string { return []string{"Pending", "Paid", "Shipped"} }
func (OrderStatus) DisplayNames() []string { return []string{"待支付", "已支付", "已发货"} }
type CreateOrderDto struct {
Sku string // 必传
Count int64 // 必传
Note *string // 可空
Status *OrderStatus // 可空,缺省 Pending
Tags []string // 可省略
}
type OrderSvc struct {
fun.Ctx
}
func (s *OrderSvc) Create(dto CreateOrderDto) (OrderDto, error) {
return OrderDto{Id: 9007199254740993, Status: OrderStatus(0), Amount: "199.00"}, nil
}
// Cancel error-only 签名:无返回数据
func (s *OrderSvc) Cancel(dto CancelDto) error {
if dto.Reason == nil {
return fun.Error(4004, "必须填写取消原因") // status=2,code/msg 原样透传
}
return nil
}生成对应客户端产物的命令在 cmd/genexample,产物见 example/gen。
生成 TypeScript 客户端
推荐单独建一个 cmd/gen,用 BindServiceForGen 代替 BindService——只登记类型元信息,不装配任何基础设施(数据库、Redis 无需运行):
go
// cmd/gen/main.go
package main
import (
"fmt"
"github.com/cyi-cc/fun"
"hello-fun" // 引入你的服务定义
)
func main() {
f := fun.New()
f.BindServiceForGen(&UserSvc{})
fun.SetOutput("./frontend/src/api") // ⚠️ 会清空整个输出目录
fun.GenCode(fun.GenTs{}) // 也可加 fun.GenGo{} 同时生成 Go 客户端
fmt.Println("生成完成")
}bash
go run ./cmd/gen产物落在 <out>/ts/ 子目录,fun.ts 是聚合入口:
ts
import { api } from './api/ts/fun'
const c = api.create('/api')
c.setState({ token: 'xxx' }) // 每个请求自动带上 state
const r = await c.userSvc.get({ id: 1 })
if (r.status === 0) {
console.log(r.data!.name) // 响应键已转小写,类型直达
}前端联调(Vite 代理)
后端只有 /cell 一个路径,vite 代理需要把前缀重写掉:
js
// vite.config.js
export default {
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (p) => p.replace(/^\/api/, ''),
},
},
},
}客户端 api.create('/api'),实际请求 POST /api/cell → 代理转给后端 /cell。
浏览器直连(无代理)场景改用服务端 CORS 白名单。
常见的「第一步」错误
| 现象 | 原因 |
|---|---|
启动即 panic:Unsupported types int | DTO 用了普通 int/uint、float、map 等非法类型,见 DTO 规则 |
运行期报 must be a pointer or have a corresponding field | 非指针、非 slice 的 DTO 字段漏传或传了 null |
客户端报 method not found | 方法名首字母小写(未导出),不会注册为端点 |
| 前端取不到字段 | 响应键已全部转首字母小写,按 camelCase 取值 |
更多见常见问题。