# 业务异常

[课件](https://gitee.com/infraboard/go-course/blob/master/zh-cn/base/exception.md)


## error 是接口

```go
type error interface {
    Error() string
}
```

## 封装 error
```go
// fmt.Errorf("token is empty")
type wrapError struct {
	msg string
	err error
}

func (e *wrapError) Error() string {
	return e.msg
}

func (e *wrapError) Unwrap() error {
	return e.err
}
```

## 自定义业务异常 

```go
func NewApiException(code int, message string) *ApiException {
	return &ApiException{
		HttpCode: 500,
		Code:     code,
		Message:  message,
		Meta:     map[string]any{},
		Data:     map[string]any{},
	}
}

// ApiException API异常结构体
// 自定义异常数据结构体, 方便后续的异常处理和日志记录
type ApiException struct {
	Service  string         `json:"service"`             // 服务名称, 你后端服务多个
	HttpCode int            `json:"http_code,omitempty"` // HTTP状态码, 需不需定义明确的http code, 默认500,非2xx
	Code     int            `json:"code"`                // 业务异常码, 具体业务自己定义
	Reason   string         `json:"reason"`              // 异常原因
	Message  string         `json:"message"`             // 详细描述
	Meta     map[string]any `json:"meta"`                // 元数据
	Data     any            `json:"data"`                // 附加数据
}

func (e *ApiException) Error() string {
	return e.Message
}

// ToJson 转为JSON字符串，方便接口返回
func (e *ApiException) ToJson() string {
	dj, _ := json.Marshal(e)
	return string(dj)
}

// 辅助函数: IsApiException 判断是否是ApiException
func IsApiException(err error, code int) bool {
	if v, ok := err.(*ApiException); ok {
		return v.Code == code
	}
	return false
}
```

## mcube ApiException库

1. 如何为你的项目引入3方包:(体验)
```sh 
# 1. 找到你需要安装的3方包: "github.com/infraboard/mcube/v2/exception"
# 2. 为你的工程安装该包: go get "github.com/infraboard/mcube/v2/exception"(项目目录)
# 3. 初始化一个go工程(go.mod的工程文件): go mod init <工程名称: business_error>
# 4. 当前 go.mod所在目录的工程，就是一个 Go标准工程
# 5. 可以使用 go get来安装3方依赖
➜  business_error git:(main) ✗ go get "github.com/infraboard/mcube/v2/exception"
go: downloading github.com/infraboard/mcube/v2 v2.0.91
go: added github.com/infraboard/mcube/v2 v2.0.91
# 	"github.com/infraboard/mcube/v2/exception",使用这个包: exception.NewApiException(10001, "token is empty")
# exception 是包的名称: 大多数情况就是 路径里面最后一个目录(package 定义的名称),  . 引入包里面的各种资源(大写开头): 函数，变量，类型(Struct,自定义类型， ...)
# 包就是把 公共的工具或者能力 维护到一个地方(包)，方便多个项目 引入使用
```

```go
package main

import (
	"encoding/json"
	"fmt"

	//
	"github.com/infraboard/mcube/v2/exception"
)

func main() {
	err := ValidateToken("")
	if err != nil {
		fmt.Println(err)
		bj, _ := json.Marshal(err)
		fmt.Println(string(bj))
		// {"service":"","http_code":500,"code":10001,"reason":"","message":"token is empty","meta":{},"data":{}}
		if exception.IsApiException(err, 10001) {
			fmt.Println("IsApiException: token is empty")
		}
	}
}

func ValidateToken(token string) error {
	if token == "" {
		return exception.NewApiException(10001, "token is empty")
	}

	return nil
}
```

