1
0
mirror of https://github.com/go-kratos/kratos.git synced 2025-11-06 08:59:18 +02:00

errors: Error v2 (#860)

* refactor api errors
This commit is contained in:
Tony Chen
2021-04-25 20:19:23 +08:00
committed by GitHub
parent 5d9b5818f2
commit 9806191b7f
14 changed files with 277 additions and 680 deletions

View File

@@ -1,317 +0,0 @@
package errors
import (
"errors"
"fmt"
)
// Cancelled The operation was cancelled, typically by the caller.
// HTTP Mapping: 499 Client Closed Request
func Cancelled(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 1,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsCancelled determines if err is an error which indicates a cancelled error.
// It supports wrapped errors.
func IsCancelled(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 1
}
return false
}
// Unknown error.
// HTTP Mapping: 500 Internal Server Error
func Unknown(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 2,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsUnknown determines if err is an error which indicates a unknown error.
// It supports wrapped errors.
func IsUnknown(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 2
}
return false
}
// InvalidArgument The client specified an invalid argument.
// HTTP Mapping: 400 Bad Request
func InvalidArgument(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 3,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsInvalidArgument determines if err is an error which indicates an invalid argument error.
// It supports wrapped errors.
func IsInvalidArgument(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 3
}
return false
}
// DeadlineExceeded The deadline expired before the operation could complete.
// HTTP Mapping: 504 Gateway Timeout
func DeadlineExceeded(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 4,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsDeadlineExceeded determines if err is an error which indicates a deadline exceeded error.
// It supports wrapped errors.
func IsDeadlineExceeded(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 4
}
return false
}
// NotFound Some requested entity (e.g., file or directory) was not found.
// HTTP Mapping: 404 Not Found
func NotFound(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 5,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsNotFound determines if err is an error which indicates a not found error.
// It supports wrapped errors.
func IsNotFound(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 5
}
return false
}
// AlreadyExists The entity that a client attempted to create (e.g., file or directory) already exists.
// HTTP Mapping: 409 Conflict
func AlreadyExists(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 6,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsAlreadyExists determines if err is an error which indicates a already exsits error.
// It supports wrapped errors.
func IsAlreadyExists(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 6
}
return false
}
// PermissionDenied The caller does not have permission to execute the specified operation.
// HTTP Mapping: 403 Forbidden
func PermissionDenied(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 7,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsPermissionDenied determines if err is an error which indicates a permission denied error.
// It supports wrapped errors.
func IsPermissionDenied(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 7
}
return false
}
// ResourceExhausted Some resource has been exhausted, perhaps a per-user quota, or
// perhaps the entire file system is out of space.
// HTTP Mapping: 429 Too Many Requests
func ResourceExhausted(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 8,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsResourceExhausted determines if err is an error which indicates a resource exhausted error.
// It supports wrapped errors.
func IsResourceExhausted(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 8
}
return false
}
// FailedPrecondition The operation was rejected because the system is not in a state
// required for the operation's execution.
// HTTP Mapping: 400 Bad Request
func FailedPrecondition(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 9,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsFailedPrecondition determines if err is an error which indicates a failed precondition error.
// It supports wrapped errors.
func IsFailedPrecondition(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 9
}
return false
}
// Aborted The operation was aborted, typically due to a concurrency issue such as
// a sequencer check failure or transaction abort.
// HTTP Mapping: 409 Conflict
func Aborted(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 10,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsAborted determines if err is an error which indicates an aborted error.
// It supports wrapped errors.
func IsAborted(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 10
}
return false
}
// OutOfRange The operation was attempted past the valid range. E.g., seeking or
// reading past end-of-file.
// HTTP Mapping: 400 Bad Request
func OutOfRange(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 11,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsOutOfRange determines if err is an error which indicates a out of range error.
// It supports wrapped errors.
func IsOutOfRange(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 11
}
return false
}
// Unimplemented The operation is not implemented or is not supported/enabled in this service.
// HTTP Mapping: 501 Not Implemented
func Unimplemented(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 12,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsUnimplemented determines if err is an error which indicates a unimplemented error.
// It supports wrapped errors.
func IsUnimplemented(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 12
}
return false
}
// Internal This means that some invariants expected by the
// underlying system have been broken. This error code is reserved
// for serious errors.
//
// HTTP Mapping: 500 Internal Server Error
func Internal(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 13,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsInternal determines if err is an error which indicates an internal server error.
// It supports wrapped errors.
func IsInternal(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 13
}
return false
}
// Unavailable The service is currently unavailable.
// HTTP Mapping: 503 Service Unavailable
func Unavailable(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 14,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsUnavailable determines if err is an error which indicates a unavailable error.
// It supports wrapped errors.
func IsUnavailable(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 14
}
return false
}
// DataLoss Unrecoverable data loss or corruption.
// HTTP Mapping: 500 Internal Server Error
func DataLoss(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 15,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsDataLoss determines if err is an error which indicates a data loss error.
// It supports wrapped errors.
func IsDataLoss(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 15
}
return false
}
// Unauthorized The request does not have valid authentication credentials for the operation.
// HTTP Mapping: 401 Unauthorized
func Unauthorized(reason, format string, a ...interface{}) error {
return &StatusError{
Code: 16,
Reason: reason,
Message: fmt.Sprintf(format, a...),
}
}
// IsUnauthorized determines if err is an error which indicates a unauthorized error.
// It supports wrapped errors.
func IsUnauthorized(err error) bool {
if se := new(StatusError); errors.As(err, &se) {
return se.Code == 16
}
return false
}

View File

@@ -7,110 +7,83 @@ import (
)
const (
// UnknownReason is unknown reason for error info.
UnknownReason = ""
// SupportPackageIsVersion1 this constant should not be referenced by any other code.
SupportPackageIsVersion1 = true
)
var _ error = (*StatusError)(nil)
// Error contains an error response from the server.
// For more details see https://github.com/go-kratos/kratos/issues/858.
type Error struct {
Code int `json:"code"`
Domain string `json:"domain"`
Reason string `json:"reason"`
Message string `json:"message"`
Metadata map[string]string `json:"metadata"`
}
// StatusError contains an error response from the server.
type StatusError = Status
// WithMetadata with an MD formed by the mapping of key, value.
func (e *Error) WithMetadata(md map[string]string) *Error {
err := *e
err.Metadata = md
return &err
}
// Is matches each error in the chain with the target value.
func (e *StatusError) Is(target error) bool {
err, ok := target.(*StatusError)
if ok {
return e.Code == err.Code
func (e *Error) Is(err error) bool {
if target := new(Error); errors.As(err, &target) {
return target.Code == e.Code && target.Reason == e.Reason
}
return false
}
// HTTPStatus returns the Status represented by se.
func (e *StatusError) HTTPStatus() int {
switch e.Code {
case 0:
return http.StatusOK
case 1:
return http.StatusInternalServerError
case 2:
return http.StatusInternalServerError
case 3:
return http.StatusBadRequest
case 4:
return http.StatusRequestTimeout
case 5:
return http.StatusNotFound
case 6:
return http.StatusConflict
case 7:
return http.StatusForbidden
case 8:
return http.StatusTooManyRequests
case 9:
return http.StatusPreconditionFailed
case 10:
return http.StatusConflict
case 11:
return http.StatusBadRequest
case 12:
return http.StatusNotImplemented
case 13:
return http.StatusInternalServerError
case 14:
return http.StatusServiceUnavailable
case 15:
return http.StatusInternalServerError
case 16:
return http.StatusUnauthorized
default:
return http.StatusInternalServerError
}
func (e *Error) Error() string {
return fmt.Sprintf("error: code = %d domain = %s reason = %s message = %s metadata = %v", e.Code, e.Domain, e.Reason, e.Message, e.Metadata)
}
func (e *StatusError) Error() string {
return fmt.Sprintf("error: code = %d reason = %s message = %s details = %+v", e.Code, e.Reason, e.Message, e.Details)
}
// Error returns a Status representing c and msg.
func Error(code int32, reason, message string) error {
return &StatusError{
// New returns an error object for the code, message and error info.
func New(code int, reason, message string) *Error {
return &Error{
Code: code,
Reason: reason,
Message: message,
}
}
// Errorf returns New(c, fmt.Sprintf(format, a...)).
func Errorf(code int32, reason, format string, a ...interface{}) error {
return Error(code, reason, fmt.Sprintf(format, a...))
}
// Code returns the status code.
func Code(err error) int32 {
// Code returns the code for a particular error.
// It supports wrapped errors.
func Code(err error) int {
if err == nil {
return 0 // ok
return http.StatusOK
}
if se := new(StatusError); errors.As(err, &se) {
return se.Code
if target := new(Error); errors.As(err, &target) {
return target.Code
}
return 2 // unknown
return http.StatusInternalServerError
}
// Reason returns the status for a particular error.
// Domain returns the domain for a particular error.
// It supports wrapped errors.
func Domain(err error) string {
if target := new(Error); errors.As(err, &target) {
return target.Domain
}
return ""
}
// Reason returns the reason for a particular error.
// It supports wrapped errors.
func Reason(err error) string {
if se := new(StatusError); errors.As(err, &se) {
return se.Reason
if target := new(Error); errors.As(err, &target) {
return target.Reason
}
return UnknownReason
return ""
}
// FromError returns status error.
func FromError(err error) (*StatusError, bool) {
if se := new(StatusError); errors.As(err, &se) {
return se, true
// FromError try to convert an error to *Error.
// It supports wrapped errors.
func FromError(err error) *Error {
if target := new(Error); errors.As(err, &target) {
return target
}
return nil, false
return New(http.StatusInternalServerError, "", err.Error())
}

View File

@@ -1,187 +0,0 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
// protoc-gen-go v1.25.0
// protoc v3.13.0
// source: errors.proto
package errors
import (
proto "github.com/golang/protobuf/proto"
any "github.com/golang/protobuf/ptypes/any"
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
reflect "reflect"
sync "sync"
)
const (
// Verify that this generated code is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
// Verify that runtime/protoimpl is sufficiently up-to-date.
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
)
// This is a compile-time assertion that a sufficiently up-to-date version
// of the legacy proto package is being used.
const _ = proto.ProtoPackageIsVersion4
type Status struct {
state protoimpl.MessageState
sizeCache protoimpl.SizeCache
unknownFields protoimpl.UnknownFields
Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"`
Reason string `protobuf:"bytes,2,opt,name=reason,proto3" json:"reason,omitempty"`
Message string `protobuf:"bytes,3,opt,name=message,proto3" json:"message,omitempty"`
Details []*any.Any `protobuf:"bytes,4,rep,name=details,proto3" json:"details,omitempty"`
}
func (x *Status) Reset() {
*x = Status{}
if protoimpl.UnsafeEnabled {
mi := &file_errors_proto_msgTypes[0]
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
ms.StoreMessageInfo(mi)
}
}
func (x *Status) String() string {
return protoimpl.X.MessageStringOf(x)
}
func (*Status) ProtoMessage() {}
func (x *Status) ProtoReflect() protoreflect.Message {
mi := &file_errors_proto_msgTypes[0]
if protoimpl.UnsafeEnabled && x != nil {
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
if ms.LoadMessageInfo() == nil {
ms.StoreMessageInfo(mi)
}
return ms
}
return mi.MessageOf(x)
}
// Deprecated: Use Status.ProtoReflect.Descriptor instead.
func (*Status) Descriptor() ([]byte, []int) {
return file_errors_proto_rawDescGZIP(), []int{0}
}
func (x *Status) GetCode() int32 {
if x != nil {
return x.Code
}
return 0
}
func (x *Status) GetReason() string {
if x != nil {
return x.Reason
}
return ""
}
func (x *Status) GetMessage() string {
if x != nil {
return x.Message
}
return ""
}
func (x *Status) GetDetails() []*any.Any {
if x != nil {
return x.Details
}
return nil
}
var File_errors_proto protoreflect.FileDescriptor
var file_errors_proto_rawDesc = []byte{
0x0a, 0x0c, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x0d,
0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x1a, 0x19, 0x67,
0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x61,
0x6e, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x7e, 0x0a, 0x06, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x12, 0x12, 0x0a, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x05,
0x52, 0x04, 0x63, 0x6f, 0x64, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x12, 0x18,
0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52,
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x62, 0x0a, 0x11, 0x64, 0x65, 0x76, 0x2e,
0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2e, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x42, 0x0b, 0x45,
0x72, 0x72, 0x6f, 0x72, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2c, 0x67, 0x69,
0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x67, 0x6f, 0x2d, 0x6b, 0x72, 0x61, 0x74,
0x6f, 0x73, 0x2f, 0x6b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x2f, 0x76, 0x32, 0x2f, 0x65, 0x72, 0x72,
0x6f, 0x72, 0x73, 0x3b, 0x65, 0x72, 0x72, 0x6f, 0x72, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x0c,
0x4b, 0x72, 0x61, 0x74, 0x6f, 0x73, 0x45, 0x72, 0x72, 0x6f, 0x72, 0x73, 0x62, 0x06, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x33,
}
var (
file_errors_proto_rawDescOnce sync.Once
file_errors_proto_rawDescData = file_errors_proto_rawDesc
)
func file_errors_proto_rawDescGZIP() []byte {
file_errors_proto_rawDescOnce.Do(func() {
file_errors_proto_rawDescData = protoimpl.X.CompressGZIP(file_errors_proto_rawDescData)
})
return file_errors_proto_rawDescData
}
var file_errors_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
var file_errors_proto_goTypes = []interface{}{
(*Status)(nil), // 0: kratos.errors.Status
(*any.Any)(nil), // 1: google.protobuf.Any
}
var file_errors_proto_depIdxs = []int32{
1, // 0: kratos.errors.Status.details:type_name -> google.protobuf.Any
1, // [1:1] is the sub-list for method output_type
1, // [1:1] is the sub-list for method input_type
1, // [1:1] is the sub-list for extension type_name
1, // [1:1] is the sub-list for extension extendee
0, // [0:1] is the sub-list for field type_name
}
func init() { file_errors_proto_init() }
func file_errors_proto_init() {
if File_errors_proto != nil {
return
}
if !protoimpl.UnsafeEnabled {
file_errors_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
switch v := v.(*Status); i {
case 0:
return &v.state
case 1:
return &v.sizeCache
case 2:
return &v.unknownFields
default:
return nil
}
}
}
type x struct{}
out := protoimpl.TypeBuilder{
File: protoimpl.DescBuilder{
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
RawDescriptor: file_errors_proto_rawDesc,
NumEnums: 0,
NumMessages: 1,
NumExtensions: 0,
NumServices: 0,
},
GoTypes: file_errors_proto_goTypes,
DependencyIndexes: file_errors_proto_depIdxs,
MessageInfos: file_errors_proto_msgTypes,
}.Build()
File_errors_proto = out.File
file_errors_proto_rawDesc = nil
file_errors_proto_goTypes = nil
file_errors_proto_depIdxs = nil
}

View File

@@ -1,19 +0,0 @@
syntax = "proto3";
package kratos.errors;
import "google/protobuf/any.proto";
option cc_enable_arenas = true;
option go_package = "github.com/go-kratos/kratos/v2/errors;errors";
option java_multiple_files = true;
option java_outer_classname = "ErrorsProto";
option java_package = "com.github.kratos.errors";
option objc_class_prefix = "KratosErrors";
message Status {
int32 code = 1;
string reason = 2;
string message = 3;
repeated google.protobuf.Any details = 4;
}

View File

@@ -6,49 +6,45 @@ import (
"testing"
)
func TestErrorsMatch(t *testing.T) {
s := &StatusError{Code: 1}
st := &StatusError{Code: 2}
func TestError(t *testing.T) {
var (
base *Error
)
err := New(400, "reason", "message")
err2 := New(400, "reason", "message")
err3 := err.WithMetadata(map[string]string{
"foo": "bar",
})
werr := fmt.Errorf("wrap %w", err)
if errors.Is(s, st) {
t.Errorf("error is not match: %+v -> %+v", s, st)
if errors.Is(err, new(Error)) {
t.Errorf("should not be equal: %v", err)
}
if !errors.Is(werr, err) {
t.Errorf("should be equal: %v", err)
}
if !errors.Is(werr, err2) {
t.Errorf("should be equal: %v", err)
}
s.Code = 1
st.Code = 1
if !errors.Is(s, st) {
t.Errorf("error is not match: %+v -> %+v", s, st)
if !errors.As(err, &base) {
t.Errorf("should be matchs: %v", err)
}
if !IsBadRequest(err) {
t.Errorf("should be matchs: %v", err)
}
s.Reason = "test_reason"
s.Reason = "test_reason"
if !errors.Is(s, st) {
t.Errorf("error is not match: %+v -> %+v", s, st)
if code := Code(err); code != err2.Code {
t.Errorf("got %d want: %s", code, err)
}
if domain := Domain(err); domain != err2.Domain {
t.Errorf("got %s want: %s", domain, err)
}
if reason := Reason(err); reason != err3.Reason {
t.Errorf("got %s want: %s", reason, err)
}
if Reason(s) != "test_reason" {
t.Errorf("error is not match: %+v -> %+v", s, st)
}
}
func TestErrorIs(t *testing.T) {
err1 := &StatusError{Code: 1}
t.Log(err1)
err2 := fmt.Errorf("wrap : %w", err1)
t.Log(err2)
if !(errors.Is(err2, err1)) {
t.Errorf("error is not match: a: %v b: %v ", err2, err1)
}
}
func TestErrorAs(t *testing.T) {
err1 := &StatusError{Code: 1}
err2 := fmt.Errorf("wrap : %w", err1)
err3 := new(StatusError)
if !errors.As(err2, &err3) {
t.Errorf("error is not match: %v", err2)
if err3.Metadata["foo"] != "bar" {
t.Error("not expected metadata")
}
}

80
errors/types.go Normal file
View File

@@ -0,0 +1,80 @@
package errors
import "net/http"
// BadRequest new BadRequest error that is mapped to a 400 response.
func BadRequest(reason, message string) *Error {
return New(http.StatusBadRequest, reason, message)
}
// IsBadRequest determines if err is an error which indicates a BadRequest error.
// It supports wrapped errors.
func IsBadRequest(err error) bool {
return Code(err) == http.StatusBadRequest
}
// Unauthorized new Unauthorized error that is mapped to a 401 response.
func Unauthorized(reason, message string) *Error {
return New(http.StatusUnauthorized, reason, message)
}
// IsUnauthorized determines if err is an error which indicates a Unauthorized error.
// It supports wrapped errors.
func IsUnauthorized(err error) bool {
return Code(err) == http.StatusUnauthorized
}
// Forbidden new Forbidden error that is mapped to a 403 response.
func Forbidden(reason, message string) *Error {
return New(http.StatusForbidden, reason, message)
}
// IsForbidden determines if err is an error which indicates a Forbidden error.
// It supports wrapped errors.
func IsForbidden(err error) bool {
return Code(err) == http.StatusForbidden
}
// NotFound new NotFound error that is mapped to a 404 response.
func NotFound(reason, message string) *Error {
return New(http.StatusNotFound, reason, message)
}
// IsNotFound determines if err is an error which indicates an NotFound error.
// It supports wrapped errors.
func IsNotFound(err error) bool {
return Code(err) == http.StatusNotFound
}
// Conflict new Conflict error that is mapped to a 409 response.
func Conflict(reason, message string) *Error {
return New(http.StatusConflict, reason, message)
}
// IsConflict determines if err is an error which indicates a Conflict error.
// It supports wrapped errors.
func IsConflict(err error) bool {
return Code(err) == http.StatusConflict
}
// InternalServer new InternalServer error that is mapped to a 500 response.
func InternalServer(reason, message string) *Error {
return New(http.StatusInternalServerError, reason, message)
}
// IsInternalServer determines if err is an error which indicates an InternalServer error.
// It supports wrapped errors.
func IsInternalServer(err error) bool {
return Code(err) == http.StatusInternalServerError
}
// ServiceUnavailable new ServiceUnavailable error that is mapped to a HTTP 503 response.
func ServiceUnavailable(reason, message string) *Error {
return New(http.StatusServiceUnavailable, reason, message)
}
// IsServiceUnavailable determines if err is an error which indicates a ServiceUnavailable error.
// It supports wrapped errors.
func IsServiceUnavailable(err error) bool {
return Code(err) == http.StatusServiceUnavailable
}

32
errors/types_test.go Normal file
View File

@@ -0,0 +1,32 @@
package errors
import "testing"
func TestTypes(t *testing.T) {
var (
input = []*Error{
BadRequest("reason_400", "message_400"),
Unauthorized("reason_401", "message_401"),
Forbidden("reason_403", "message_403"),
NotFound("reason_404", "message_404"),
Conflict("reason_409", "message_409"),
InternalServer("reason_500", "message_500"),
ServiceUnavailable("reason_503", "message_503"),
}
output = []func(error) bool{
IsBadRequest,
IsUnauthorized,
IsForbidden,
IsNotFound,
IsConflict,
IsInternalServer,
IsServiceUnavailable,
}
)
for i, in := range input {
if !output[i](in) {
t.Errorf("not expect: %v", in)
}
}
}