OSDN Git Service

b902c9f3b9da88ed745971b3ae3de73c0a5ab61d
[bytom/vapor.git] / net / http / httperror / httperror.go
1 // Package httperror defines the format for HTTP error responses
2 // from Chain services.
3 package httperror
4
5 import (
6         "context"
7         "net/http"
8
9         "github.com/vapor/errors"
10         "github.com/vapor/net/http/httpjson"
11 )
12
13 // Info contains a set of error codes to send to the user.
14 type Info struct {
15         HTTPStatus int    `json:"-"`
16         ChainCode  string `json:"code"`
17         Message    string `json:"msg"`
18 }
19
20 // Response defines the error response for a Chain error.
21 type Response struct {
22         Info
23         Status    string                 `json:"status,omitempty"`
24         Detail    string                 `json:"detail,omitempty"`
25         Data      map[string]interface{} `json:"data,omitempty"`
26         Temporary bool                   `json:"temporary"`
27 }
28
29 // Formatter defines rules for mapping errors to the Chain error
30 // response format.
31 type Formatter struct {
32         Default     Info
33         IsTemporary func(info Info, err error) bool
34         Errors      map[error]Info
35 }
36
37 // Format builds an error Response body describing err by consulting
38 // the f.Errors lookup table. If no entry is found, it returns f.Default.
39 func (f Formatter) Format(err error) (body Response) {
40         root := errors.Root(err)
41         // Some types cannot be used as map keys, for example slices.
42         // If an error's underlying type is one of these, don't panic.
43         // Just treat it like any other missing entry.
44         defer func() {
45                 if err := recover(); err != nil {
46                         body = Response{f.Default, "fail", "", nil, true}
47                 }
48         }()
49         info, ok := f.Errors[root]
50         if !ok {
51                 info = f.Default
52         }
53
54         body = Response{
55                 Info:      info,
56                 Status:    "fail",
57                 Detail:    errors.Detail(err),
58                 Data:      errors.Data(err),
59                 Temporary: f.IsTemporary(info, err),
60         }
61         return body
62 }
63
64 // Write writes a json encoded Response to the ResponseWriter.
65 // It uses the status code associated with the error.
66 //
67 // Write may be used as an ErrorWriter in the httpjson package.
68 func (f Formatter) Write(ctx context.Context, w http.ResponseWriter, err error) {
69         resp := f.Format(err)
70         httpjson.Write(ctx, w, resp.HTTPStatus, resp)
71 }