Error type
What is the best way to return an RPC error?
Overview#
It is always not easy to define a user-friendly API. However, errors could happen anywhere.
In order to return standard error type to user, it is important to define an error type.
By default, panic middleware will handle unexpected panic and respond with google style error format:
- Google style
{
"error":{
"code":500,
"status":"Internal Server Error",
"message":"Panic occurs",
"details":[
"panic manually"
]
}
}
- Amazon style
{
"response":{
"errors":[
{
"error":{
"code":500,
"status":"Internal Server Error",
"message":"Panic occurs",
"details":[
"panic manually"
]
}
}
]
}
}
- Custom style
User can also define your own style which will be introduced bellow.
Quick start#
In this example, we didn't set errorModel option in middleware, google style will be used by default.
1.Install#
$ go get github.com/rookie-ninja/rk-boot/v2
$ go get github.com/rookie-ninja/rk-echo
2.Create boot.yaml#
echo:
- name: greeter
port: 8080
enabled: true
3.Create 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
}
4.Validate#
$ 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 style error code#
- 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#
Custom style#
User can register custom error builder and register it into Entry.
Please implement bellow interface.
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.Validate#
$ curl "localhost:8080/v1/greeter?name=rk-dev"
{
"ErrCode":208,
"ErrMsg":"Trigger manually!",
"ErrDetails":[
"This is detail.",
false,
-1,
0.1
]
}