Kratos 錯誤處理

2022-04-24 12:00 更新

錯誤處理

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):

  • 當(dāng)枚舉組沒有配置缺省錯誤碼時, 當(dāng)前枚舉組的沒有配置錯誤碼的枚舉值會被忽略
  • 當(dāng)整個枚舉組都沒配置錯誤碼時,當(dāng)前枚舉組會被忽略
  • 錯誤碼的取值范圍應(yīng)該在 0 < code <= 600 之間, 超出范圍將拋出異常

錯誤生成

通過 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...))
}

使用方式

響應(yīng)錯誤

當(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
})


以上內(nèi)容是否對您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號
微信公眾號

編程獅公眾號