APIs 響應(yīng)錯誤時可以直接使用 errors 包中的 New 方法來聲明一個 error,也可以直接通過 proto 預(yù)定義定義錯誤碼,然后通過 proto-gen-go 生成幫助代碼,直接返回 error。
在errors包中,錯誤模型主要跟 gRPC 狀態(tài)碼一致,并且 Error 實(shí)現(xiàn)了 GRPCStatus() 接口, 實(shí)現(xiàn)了 grpc 和 http 錯誤碼的轉(zhuǎn)換, 業(yè)務(wù)原因通過 ErrorInfo 返回:
{
// 錯誤碼,跟 http-status 一致,并且在 grpc 中可以轉(zhuǎn)換成 grpc-status
"code": 500,
// 錯誤原因,定義為業(yè)務(wù)判定錯誤碼
"reason": "USER_NOT_FOUND",
// 錯誤信息,為用戶可讀的信息,可作為用戶提示內(nèi)容
"message": "invalid argument error",
// 錯誤元信息,為錯誤添加附加可擴(kuò)展信息
"metadata": {
"foo": "bar"
}
}
# 如果電腦中沒有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";
// 多語言特定包名,用于源代碼引用
option go_package = "kratos/api/helloworld;helloworld";
option java_multiple_files = true;
option java_package = "api.helloworld";
enum ErrorReason {
// 設(shè)置缺省錯誤碼
option (errors.default_code) = 500;
// 為某個枚舉單獨(dú)設(shè)置錯誤碼
USER_NOT_FOUND = 0 [(errors.code) = 404];
CONTENT_MISSING = 1 [(errors.code) = 400];
}
注意事項(xiàng):
通過 proto 生成對應(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í)行成功之后,會在 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)錯誤時,可以通過使用 kratos errors 包中的 New 方法來響應(yīng)錯誤, 或者可以通過proto定義,然后通過 protoc-gen-go-error 工具生成幫助代碼來響應(yīng)錯誤
// 通過 errors.New() 響應(yīng)錯誤
errors.New(500, "USER_NAME_EMPTY", "user name is empty")
// 通過 proto 生成的代碼響應(yīng)錯誤,并且包名應(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()
// 通過 errors.Is() 斷言
if errors.Is(err,errors.BadRequest("USER_NAME_EMPTY","")) {
// do something
}
// 通過判斷 *Error.Reason 和 *Error.Code
e := errors.FromError(err)
if e.Reason == "USER_NAME_EMPTY" && e.Code == 500 {
// do something
}
// 通過 proto 生成的代碼斷言錯誤,并且包名應(yīng)替換為自己生成代碼后的 package name(此處對應(yīng)上面生成的 helloworld 包,調(diào)用定義的方法)
if helloworld.IsUserNotFound(err) {
// do something
})
更多建議: