APIs 響應(yīng)錯(cuò)誤時(shí)可以直接使用 errors 包中的 New 方法來(lái)聲明一個(gè) error,也可以直接通過(guò) proto 預(yù)定義定義錯(cuò)誤碼,然后通過(guò) proto-gen-go 生成幫助代碼,直接返回 error。
在errors包中,錯(cuò)誤模型主要跟 gRPC 狀態(tài)碼一致,并且 Error 實(shí)現(xiàn)了 GRPCStatus() 接口, 實(shí)現(xiàn)了 grpc 和 http 錯(cuò)誤碼的轉(zhuǎn)換, 業(yè)務(wù)原因通過(guò) ErrorInfo 返回:
{
// 錯(cuò)誤碼,跟 http-status 一致,并且在 grpc 中可以轉(zhuǎn)換成 grpc-status
"code": 500,
// 錯(cuò)誤原因,定義為業(yè)務(wù)判定錯(cuò)誤碼
"reason": "USER_NOT_FOUND",
// 錯(cuò)誤信息,為用戶可讀的信息,可作為用戶提示內(nèi)容
"message": "invalid argument error",
// 錯(cuò)誤元信息,為錯(cuò)誤添加附加可擴(kuò)展信息
"metadata": {
"foo": "bar"
}
}
# 如果電腦中沒(méi)有protoc-gen-go需要先安裝
# go install google.golang.org/protobuf/cmd/protoc-gen-go
go install github.com/go-kratos/kratos/cmd/protoc-gen-go-errors/v2
api/helloworld/v1/helloworld.proto
syntax = "proto3";
// 定義包名
package api.kratos.v1;
import "errors/errors.proto";
// 多語(yǔ)言特定包名,用于源代碼引用
option go_package = "kratos/api/helloworld;helloworld";
option java_multiple_files = true;
option java_package = "api.helloworld";
enum ErrorReason {
// 設(shè)置缺省錯(cuò)誤碼
option (errors.default_code) = 500;
// 為某個(gè)枚舉單獨(dú)設(shè)置錯(cuò)誤碼
USER_NOT_FOUND = 0 [(errors.code) = 404];
CONTENT_MISSING = 1 [(errors.code) = 400];
}
注意事項(xiàng):
通過(guò) proto 生成對(duì)應(yīng)的代碼:
protoc --proto_path=. \
--proto_path=./third_party \
--go_out=paths=source_relative:. \
--go-errors_out=paths=source_relative:. \
$(API_PROTO_FILES)
或者在項(xiàng)目根目錄使用Makefile指令
make errors
執(zhí)行成功之后,會(huì)在 api/helloworld 目錄下生成 helloworld_errors.pb.go 文件,代碼如下:
package helloworld
import (
fmt "fmt"
errors "github.com/go-kratos/kratos/v2/errors"
)
// This is a compile-time assertion to ensure that this generated file
// is compatible with the kratos package it is being compiled against.
const _ = errors.SupportPackageIsVersion1
func IsUserNotFound(err error) bool {
if err == nil {
return false
}
e := errors.FromError(err)
return e.Reason == ErrorReason_USER_NOT_FOUND.String() && e.Code == 404
}
func ErrorUserNotFound(format string, args ...interface{}) *errors.Error {
return errors.New(404, ErrorReason_USER_NOT_FOUND.String(), fmt.Sprintf(format, args...))
}
func IsContentMissing(err error) bool {
if err == nil {
return false
}
e := errors.FromError(err)
return e.Reason == ErrorReason_CONTENT_MISSING.String() && e.Code == 400
}
func ErrorContentMissing(format string, args ...interface{}) *errors.Error {
return errors.New(400, ErrorReason_CONTENT_MISSING.String(), fmt.Sprintf(format, args...))
}
當(dāng)業(yè)務(wù)邏輯中需要響應(yīng)錯(cuò)誤時(shí),可以通過(guò)使用 kratos errors 包中的 New 方法來(lái)響應(yīng)錯(cuò)誤, 或者可以通過(guò)proto定義,然后通過(guò) protoc-gen-go-error 工具生成幫助代碼來(lái)響應(yīng)錯(cuò)誤
// 通過(guò) errors.New() 響應(yīng)錯(cuò)誤
errors.New(500, "USER_NAME_EMPTY", "user name is empty")
// 通過(guò) proto 生成的代碼響應(yīng)錯(cuò)誤,并且包名應(yīng)替換為自己生成代碼后的 package name
api.ErrorUserNotFound("user %s not found", "kratos")
// 傳遞metadata
err := errors.New(500, "USER_NAME_EMPTY", "user name is empty")
err = err.WithMetadata(map[string]string{
"foo": "bar",
})
// 引入 helloworld 包
import "kratos/api/helloworld"
err := wrong()
// 通過(guò) errors.Is() 斷言
if errors.Is(err,errors.BadRequest("USER_NAME_EMPTY","")) {
// do something
}
// 通過(guò)判斷 *Error.Reason 和 *Error.Code
e := errors.FromError(err)
if e.Reason == "USER_NAME_EMPTY" && e.Code == 500 {
// do something
}
// 通過(guò) proto 生成的代碼斷言錯(cuò)誤,并且包名應(yīng)替換為自己生成代碼后的 package name(此處對(duì)應(yīng)上面生成的 helloworld 包,調(diào)用定義的方法)
if helloworld.IsUserNotFound(err) {
// do something
})
更多建議: