129 lines
3.7 KiB
Markdown
129 lines
3.7 KiB
Markdown
|
|
# 处理器错误处理解决方案
|
|||
|
|
|
|||
|
|
## 问题描述
|
|||
|
|
|
|||
|
|
在使用 `errors.Join(processors.ErrInvalidParam, err)` 包装错误后,外层的 `errors.Is(err, processors.ErrInvalidParam)` 无法正确识别错误类型。
|
|||
|
|
|
|||
|
|
## 原因分析
|
|||
|
|
|
|||
|
|
`fmt.Errorf` 创建的包装错误虽然实现了 `Unwrap()` 接口,但没有实现 `Is()` 接口,因此 `errors.Is` 无法正确判断错误类型。
|
|||
|
|
|
|||
|
|
## 解决方案
|
|||
|
|
|
|||
|
|
### 🎯 **推荐方案:使用 `errors.Join`(Go 1.20+)**
|
|||
|
|
|
|||
|
|
这是最简洁、最标准的解决方案,Go 1.20+ 原生支持:
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 在处理器中创建错误
|
|||
|
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
|||
|
|
|
|||
|
|
// 在应用服务层判断错误
|
|||
|
|
if errors.Is(err, processors.ErrInvalidParam) {
|
|||
|
|
// 现在可以正确识别了!
|
|||
|
|
businessError = ErrInvalidParam
|
|||
|
|
return ErrInvalidParam
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### ✅ **优势**
|
|||
|
|
|
|||
|
|
1. **极简代码**:一行代码解决问题
|
|||
|
|
2. **标准库支持**:Go 1.20+ 原生功能
|
|||
|
|
3. **完全兼容**:`errors.Is` 可以正确识别错误类型
|
|||
|
|
4. **性能优秀**:标准库实现,性能最佳
|
|||
|
|
5. **向后兼容**:现有的错误处理代码无需修改
|
|||
|
|
|
|||
|
|
### 📝 **使用方法**
|
|||
|
|
|
|||
|
|
#### 在处理器中(替换旧方式):
|
|||
|
|
```go
|
|||
|
|
// 旧方式 ❌
|
|||
|
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
|||
|
|
|
|||
|
|
// 新方式 ✅
|
|||
|
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
#### 在应用服务层(现在可以正确工作):
|
|||
|
|
```go
|
|||
|
|
if errors.Is(err, processors.ErrInvalidParam) {
|
|||
|
|
// 现在可以正确识别了!
|
|||
|
|
businessError = ErrInvalidParam
|
|||
|
|
return ErrInvalidParam
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 其他方案对比
|
|||
|
|
|
|||
|
|
### 方案1:`errors.Join`(推荐 ⭐⭐⭐⭐⭐)
|
|||
|
|
- **简洁度**:⭐⭐⭐⭐⭐
|
|||
|
|
- **兼容性**:⭐⭐⭐⭐⭐
|
|||
|
|
- **性能**:⭐⭐⭐⭐⭐
|
|||
|
|
- **维护性**:⭐⭐⭐⭐⭐
|
|||
|
|
|
|||
|
|
### 方案2:自定义错误类型
|
|||
|
|
- **简洁度**:⭐⭐⭐
|
|||
|
|
- **兼容性**:⭐⭐⭐⭐⭐
|
|||
|
|
- **性能**:⭐⭐⭐⭐
|
|||
|
|
- **维护性**:⭐⭐⭐
|
|||
|
|
|
|||
|
|
### 方案3:继续使用 `fmt.Errorf`
|
|||
|
|
- **简洁度**:⭐⭐⭐⭐
|
|||
|
|
- **兼容性**:❌(无法识别错误类型)
|
|||
|
|
- **性能**:⭐⭐⭐⭐
|
|||
|
|
- **维护性**:❌
|
|||
|
|
|
|||
|
|
## 迁移指南
|
|||
|
|
|
|||
|
|
### 步骤1: 检查Go版本
|
|||
|
|
确保项目使用 Go 1.20 或更高版本
|
|||
|
|
|
|||
|
|
### 步骤2: 更新错误创建
|
|||
|
|
将所有处理器中的 `fmt.Errorf("%s: %w", processors.ErrXXX, err)` 替换为 `errors.Join(processors.ErrXXX, err)`
|
|||
|
|
|
|||
|
|
### 步骤3: 验证错误判断
|
|||
|
|
确保应用服务层的 `errors.Is(err, processors.ErrXXX)` 能正确工作
|
|||
|
|
|
|||
|
|
### 步骤4: 测试验证
|
|||
|
|
运行测试确保所有错误处理逻辑正常工作
|
|||
|
|
|
|||
|
|
## 示例
|
|||
|
|
|
|||
|
|
```go
|
|||
|
|
// 处理器层
|
|||
|
|
func ProcessRequest(ctx context.Context, params []byte, deps *ProcessorDependencies) ([]byte, error) {
|
|||
|
|
if err := deps.Validator.ValidateStruct(paramsDto); err != nil {
|
|||
|
|
return nil, errors.Join(processors.ErrInvalidParam, err)
|
|||
|
|
}
|
|||
|
|
// ... 其他逻辑
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 应用服务层
|
|||
|
|
if err := s.apiRequestService.PreprocessRequestApi(ctx, cmd.ApiName, requestParams, &cmd.Options, callContext); err != nil {
|
|||
|
|
if errors.Is(err, processors.ErrInvalidParam) {
|
|||
|
|
// 现在可以正确识别了!
|
|||
|
|
businessError = ErrInvalidParam
|
|||
|
|
return ErrInvalidParam
|
|||
|
|
}
|
|||
|
|
// ... 其他错误处理
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
1. **Go版本要求**:需要 Go 1.20 或更高版本
|
|||
|
|
2. **错误消息格式**:`errors.Join` 使用换行符分隔多个错误
|
|||
|
|
3. **完全兼容**:`errors.Is` 现在可以正确识别所有错误类型
|
|||
|
|
4. **性能提升**:标准库实现,性能优于自定义解决方案
|
|||
|
|
|
|||
|
|
## 总结
|
|||
|
|
|
|||
|
|
使用 `errors.Join` 是最简洁、最标准的解决方案:
|
|||
|
|
- ✅ 一行代码解决问题
|
|||
|
|
- ✅ 完全兼容 `errors.Is`
|
|||
|
|
- ✅ Go 1.20+ 原生支持
|
|||
|
|
- ✅ 性能优秀,维护简单
|
|||
|
|
|
|||
|
|
如果你的项目使用 Go 1.20+,强烈推荐使用这个方案!
|