github.com/GuanceCloud/cliutils@v1.1.21/network/http/err.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  //nolint:golint,stylecheck
     7  package http
     8  
     9  import (
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	nhttp "net/http"
    14  	"strings"
    15  
    16  	"github.com/gin-gonic/gin"
    17  )
    18  
    19  var (
    20  	DefaultNamespace                 = ""
    21  	ErrUnexpectedInternalServerError = NewErr(errors.New(`unexpected internal server error`), nhttp.StatusInternalServerError)
    22  )
    23  
    24  type HttpError struct {
    25  	ErrCode  string `json:"error_code,omitempty"`
    26  	Err      error  `json:"-"`
    27  	HttpCode int    `json:"-"`
    28  }
    29  
    30  type BodyResp struct {
    31  	*HttpError
    32  	Message string      `json:"message,omitempty"`
    33  	Content interface{} `json:"content,omitempty"`
    34  }
    35  
    36  func NewNamespaceErr(err error, httpCode int, namespace string) *HttpError {
    37  	if err == nil {
    38  		return &HttpError{
    39  			HttpCode: httpCode,
    40  		}
    41  	} else {
    42  		return &HttpError{
    43  			ErrCode:  titleErr(namespace, err),
    44  			HttpCode: httpCode,
    45  			Err:      err,
    46  		}
    47  	}
    48  }
    49  
    50  func NewErr(err error, httpCode int) *HttpError {
    51  	return NewNamespaceErr(err, httpCode, DefaultNamespace)
    52  }
    53  
    54  func undefinedErr(err error) *HttpError {
    55  	return NewErr(err, nhttp.StatusInternalServerError)
    56  }
    57  
    58  func (he *HttpError) Error() string {
    59  	if he.Err == nil {
    60  		return ""
    61  	} else {
    62  		return he.Err.Error()
    63  	}
    64  }
    65  
    66  func (he *HttpError) HttpBodyPretty(c *gin.Context, body interface{}) {
    67  	if body == nil {
    68  		c.Status(he.HttpCode)
    69  		return
    70  	}
    71  
    72  	resp := &BodyResp{
    73  		HttpError: he,
    74  		Content:   body,
    75  	}
    76  
    77  	j, err := json.MarshalIndent(resp, "", "    ")
    78  	if err != nil {
    79  		undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", resp)
    80  		return
    81  	}
    82  
    83  	c.Header("X-Content-Type-Options", "nosniff")
    84  	c.Data(he.HttpCode, `application/json`, j)
    85  }
    86  
    87  func (he *HttpError) WriteBody(c *gin.Context, obj interface{}) {
    88  	if obj == nil {
    89  		c.Status(he.HttpCode)
    90  		return
    91  	}
    92  
    93  	var bodyBytes []byte
    94  	var contentType string
    95  	var err error
    96  
    97  	switch x := obj.(type) {
    98  	case []byte:
    99  		bodyBytes = x
   100  	default:
   101  		contentType = `application/json`
   102  
   103  		bodyBytes, err = json.Marshal(obj)
   104  		if err != nil {
   105  			undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", obj)
   106  			return
   107  		}
   108  	}
   109  
   110  	c.Header("X-Content-Type-Options", "nosniff")
   111  	c.Data(he.HttpCode, contentType, bodyBytes)
   112  }
   113  
   114  type RawJSONBody []byte
   115  
   116  // HttpBody Deprecated, use WriteBody.
   117  func (he *HttpError) HttpBody(c *gin.Context, body interface{}) {
   118  	if body == nil {
   119  		c.Status(he.HttpCode)
   120  		return
   121  	}
   122  
   123  	var bodyBytes []byte
   124  	var contentType string
   125  	var err error
   126  
   127  	switch x := body.(type) {
   128  	case []byte:
   129  		bodyBytes = x
   130  	case RawJSONBody:
   131  		contentType = `application/json`
   132  		bodyBytes = x
   133  	default:
   134  		resp := &BodyResp{
   135  			HttpError: he,
   136  			Content:   body,
   137  		}
   138  		contentType = `application/json`
   139  
   140  		bodyBytes, err = json.Marshal(resp)
   141  		if err != nil {
   142  			undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", resp)
   143  			return
   144  		}
   145  	}
   146  
   147  	c.Header("X-Content-Type-Options", "nosniff")
   148  	c.Data(he.HttpCode, contentType, bodyBytes)
   149  }
   150  
   151  func HttpErr(c *gin.Context, err error) {
   152  	var (
   153  		e1 *HttpError
   154  		e2 *MsgError
   155  	)
   156  
   157  	switch {
   158  	case errors.As(err, &e1):
   159  		e1.httpRespf(c, "")
   160  	case errors.As(err, &e2):
   161  		e2.HttpError.httpRespf(c, e2.Fmt, e2.Args...)
   162  	default:
   163  		undefinedErr(err).httpRespf(c, "")
   164  	}
   165  }
   166  
   167  func HttpErrf(c *gin.Context, err error, format string, args ...interface{}) {
   168  	var (
   169  		e1 *HttpError
   170  		e2 *MsgError
   171  	)
   172  
   173  	switch {
   174  	case errors.As(err, &e1):
   175  		e1.httpRespf(c, format, args...)
   176  	case errors.As(err, &e2):
   177  		e2.HttpError.httpRespf(c, format, args...)
   178  	default:
   179  		undefinedErr(err).httpRespf(c, "")
   180  	}
   181  }
   182  
   183  func (he *HttpError) httpRespf(c *gin.Context, format string, args ...interface{}) {
   184  	resp := &BodyResp{
   185  		HttpError: he,
   186  	}
   187  
   188  	resp.Message = fmt.Sprintf(format, args...)
   189  
   190  	j, err := json.Marshal(&resp)
   191  	if err != nil {
   192  		undefinedErr(err).httpRespf(c, "%s: %+#v", "json.Marshal() failed", resp)
   193  		return
   194  	}
   195  
   196  	c.Header("X-Content-Type-Options", "nosniff")
   197  	c.Data(he.HttpCode, `application/json`, j)
   198  }
   199  
   200  func titleErr(namespace string, err error) string {
   201  	if err == nil {
   202  		return ""
   203  	}
   204  
   205  	str := err.Error()
   206  	elem := strings.Split(str, ` `)
   207  
   208  	var out string
   209  	if namespace != "" {
   210  		out = namespace + `.`
   211  	}
   212  
   213  	for idx, e := range elem {
   214  		if idx == 0 {
   215  			out += e
   216  			continue
   217  		}
   218  		out += strings.Title(e) //nolint:staticcheck
   219  	}
   220  
   221  	return out
   222  }
   223  
   224  // Dynamic error create based on specific HttpError.
   225  type MsgError struct {
   226  	*HttpError
   227  	Fmt  string
   228  	Args []interface{}
   229  }
   230  
   231  func Errorf(he *HttpError, format string, args ...interface{}) *MsgError {
   232  	return &MsgError{
   233  		HttpError: he,
   234  		Fmt:       format,
   235  		Args:      args,
   236  	}
   237  }
   238  
   239  func Error(he *HttpError, msg string) *MsgError {
   240  	return &MsgError{
   241  		HttpError: he,
   242  		Fmt:       "%s",
   243  		Args:      []interface{}{msg},
   244  	}
   245  }
   246  
   247  func (me *MsgError) Error() string {
   248  	if me.HttpError != nil {
   249  		return me.Err.Error()
   250  	} else {
   251  		return ""
   252  	}
   253  }