错误码管理
rk-boot 推荐的 RPC 标准错误类型。
概述#
设计一个合理的 API 是一件不容易的事情,同时,API 还会产生各种不同的错误。
为了能让 API 使用者对于 API 的错误有一个清晰的视图,定义一个标准的 RPC 错误类型是非常重要的事情。
当使用 rk-boot 的时候,panic 中间件会默认加载到服务中,中间件会从 panic 中恢复,并且返回[内部错误]给用户。 默认使用 Google 样式的错误。
{
"error":{
"code":500,
"status":"Internal Server Error",
"message":"Panic occurs",
"details":[
"panic manually"
]
}
}
- Amazon 样式
{
"response":{
"errors":[
{
"error":{
"code":500,
"status":"Internal Server Error",
"message":"Panic occurs",
"details":[
"panic manually"
]
}
}
]
}
}
- 自定义样式
请参照后面的例子。
快速开始#
1.安装#
$ go get github.com/rookie-ninja/rk-boot/v2
$ go get github.com/rookie-ninja/rk-echo
2.创建 boot.yaml#
echo:
- name: greeter
port: 8080
enabled: true
3.创建 main.go#
package main
import (
"context"
"github.com/labstack/echo/v4"
"github.com/rookie-ninja/rk-boot/v2"
"github.com/rookie-ninja/rk-echo/boot"
"github.com/rookie-ninja/rk-entry/v2/middleware"
"net/http"
)
func main() {
// Create a new boot instance.
boot := rkboot.NewBoot()
// Add shutdown hook function
boot.AddShutdownHookFunc("shutdown-hook", func() {
fmt.Println("shutting down")
})
// Register handler
echoEntry := rkecho.GetEchoEntry("greeter")
echoEntry.Echo.GET("/v1/greeter", Greeter)
// Bootstrap
boot.Bootstrap(context.TODO())
boot.WaitForShutdownSig(context.TODO())
}
func Greeter(ctx echo.Context) error {
err := rkerror.New(http.StatusAlreadyReported, "Trigger manually!",
"This is detail.",
false, -1,
0.1)
return ctx.JSON(http.StatusAlreadyReported, err)
}
type GreeterResponse struct {
Message string
}
$ curl "localhost:8080/v1/greeter?name=rk-dev"
{
"error":{
"code":208,
"status":"Already Reported",
"message":"Trigger manually!",
"details":[
"This is detail.",
false,
-1,
0.1
]
}
}
Amazon 样式错误#
- boot.yaml
echo:
- name: greeter
port: 8080
enabled: true
middleware:
errorModel: amazon
{
"response":{
"errors":[
{
"error":{
"code":208,
"status":"Already Reported",
"message":"Trigger manually!",
"details":[
"This is detail.",
false,
-1,
0.1
]
}
}
]
}
}
Cheers#
自定义样式#
请实现如下两个接口,并注册到 Entry 中。
type ErrorInterface interface {
Error() string
Code() int
Message() string
Details() []interface{}
}
type ErrorBuilder interface {
New(code int, msg string, details ...interface{}) ErrorInterface
NewCustom() ErrorInterface
}
1.main.go#
package main
import (
"context"
"fmt"
"github.com/labstack/echo/v4"
"github.com/rookie-ninja/rk-boot/v2"
"github.com/rookie-ninja/rk-echo/boot"
"github.com/rookie-ninja/rk-entry/v2/error"
"github.com/rookie-ninja/rk-entry/v2/middleware"
"net/http"
)
func main() {
// Create a new boot instance.
boot := rkboot.NewBoot()
// Register handler
echoEntry := rkecho.GetEchoEntry("greeter")
echoEntry.Echo.GET("/v1/greeter", Greeter)
// Bootstrap
boot.Bootstrap(context.TODO())
// Set default error builder after bootstrap
rkmid.SetErrorBuilder(&MyErrorBuilder{})
boot.WaitForShutdownSig(context.TODO())
}
func Greeter(ctx echo.Context) error {
err := rkmid.GetErrorBuilder().New(http.StatusAlreadyReported, "Trigger manually!",
"This is detail.",
false, -1,
0.1)
return ctx.JSON(http.StatusAlreadyReported, err)
}
type MyError struct {
ErrCode int
ErrMsg string
ErrDetails []interface{}
}
func (m MyError) Error() string {
return fmt.Sprintf("%d-%s", m.ErrCode, m.ErrMsg)
}
func (m MyError) Code() int {
return m.ErrCode
}
func (m MyError) Message() string {
return m.ErrMsg
}
func (m MyError) Details() []interface{} {
return m.ErrDetails
}
type MyErrorBuilder struct{}
func (m *MyErrorBuilder) New(code int, msg string, details ...interface{}) rkerror.ErrorInterface {
return &MyError{
ErrCode: code,
ErrMsg: msg,
ErrDetails: details,
}
}
func (m *MyErrorBuilder) NewCustom() rkerror.ErrorInterface {
return &MyError{
ErrCode: http.StatusInternalServerError,
ErrMsg: "Internal Error",
ErrDetails: []interface{}{},
}
}
2.验证#
$ curl "localhost:8080/v1/greeter?name=rk-dev"
{
"ErrCode":208,
"ErrMsg":"Trigger manually!",
"ErrDetails":[
"This is detail.",
false,
-1,
0.1
]
}