线协议与错误处理
fun 的线协议极简:一种请求、一种响应。理解 Result 与 status 语义,前后端的错误处理就全部掌握了。
请求格式
所有业务请求都是 POST /cell,JSON body:
json
{
"serviceName": "OrderSvc",
"methodName": "create",
"data": { "sku": "A-001", "count": 2 },
"state": { "token": "eyJhbGci..." }
}| 字段 | 说明 |
|---|---|
serviceName / methodName | 端点定位,首字母大小写不敏感 |
data | DTO 参数,字段名大小写不敏感 |
state | 字符串字典,往返透传(token、请求标识等),可省略 |
非 /cell 路径返回 404,非 POST 返回 405。
响应格式:Result
go
// Go 侧定义
type Result[T any] struct {
Code *uint16 `json:"code,omitempty"`
Data *T `json:"data,omitempty"`
Msg *string `json:"msg,omitempty"`
Status uint8 `json:"status"`
}TS 客户端里的形状:
ts
export type result<T> = {
code?: number
data?: T
msg?: string
status: number
}data为 nil 时整个字段省略;空 slice 输出[]而非null;- 所有对象键递归转首字母小写,前端按 camelCase 取值。
status 语义
| status | 含义 | 谁产生 |
|---|---|---|
0 | 成功 | 服务端 |
1 | 框架 / 协议 / 基础设施失败(DTO 校验失败、JSON 非法、panic 兜底) | 服务端 |
2 | 业务失败(fun.Error(code, msg) 构造) | 服务端 |
4 | 外部请求失败或调用方取消(网络错误、HTTP 非 2xx、abort) | TS 客户端 |
5 | 明确的外部超时失败(AbortSignal.timeout 触发等) | TS 客户端 |
4 / 5 只存在于客户端归一化结果里(请求没到达业务层),服务端永远不会返回它们。
业务错误:fun.Error
go
func (s *OrderSvc) Cancel(dto CancelDto) error {
if dto.Reason == nil {
return fun.Error(4004, "必须填写取消原因") // status=2
}
return nil
}fun.Error(code, msg) 构造的错误,code / msg 原样透传给客户端:
json
{ "code": 4004, "msg": "必须填写取消原因", "status": 2 }普通 error(如 fmt.Errorf / errors.New)会归为 status=1,msg 为错误文本——要带错误码给前端时必须用 fun.Error。
框架错误的脱敏
DTO 解码失败、内部 panic 等框架级错误走 internalError 路径:
- 客户端只收到固定提示(不含 JSON 解析细节、Go 类型名等内部信息);
- 完整错误记入服务端日志(
ErrorLogger)。
TIP
排障时先看服务端日志再对照客户端收到的 msg——两者详细程度不同是刻意设计。
完整往返示例
bash
# 成功
curl -X POST localhost:8080/cell -d \
'{"serviceName":"OrderSvc","methodName":"get","data":{"Id":1}}'
# → {"status":0,"data":{"id":1,"status":1,"amount":"0.01"}}
# 业务错误
curl -X POST localhost:8080/cell -d \
'{"serviceName":"OrderSvc","methodName":"cancel","data":{"Id":1}}'
# → {"code":4004,"msg":"必须填写取消原因","status":2}
# DTO 校验失败(非指针字段漏传)
curl -X POST localhost:8080/cell -d \
'{"serviceName":"OrderSvc","methodName":"get","data":{}}'
# → {"msg":"...must be a pointer or have a corresponding field...","status":1}流式响应
流式方法的响应不是 Result:Content-Type: application/x-ndjson,每行一个 JSON(键同样转小写)。首条消息 + 后续流的签名里,T 作为第一条消息下发。详见流式响应。