-
Notifications
You must be signed in to change notification settings - Fork 0
/
problem.go
88 lines (76 loc) · 2.68 KB
/
problem.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
// Package problem implements errors similar to the ones described by RFC 7807.
package problem // import "code.soquee.net/problem"
import (
"encoding/json"
"fmt"
"net/http"
)
// Status returns a problem for the provided HTTP status code that conveys no
// additional information.
func Status(status int) Problem {
return Problem{Title: http.StatusText(status), Status: status}
}
// Problem respresents an unrecoverable error that occured during processing of
// an API request.
// Problems are designed to be embedded in application or endpoint specific
// error types.
// Consumers must use the "type" string as the primary identifier for the
// problem type.
type Problem struct {
// A URI reference that identifies the problem type.
Type string `json:"type,omitempty"`
// A short, human-readable summary of the problem type.
Title string `json:"title,omitempty"`
// The HTTP status code generated by the origin server for this occurrence of
// the problem.
Status int `json:"status,omitempty"`
// A human-readable explanation specific to this occurrence of the problem.
Detail string `json:"detail,omitempty"`
// A URI reference that identifies the specific occurrence of the problem.
Instance string `json:"instance,omitempty"`
}
// HTTPStatus returns the HTTP status as set by the origin server before
// encoding the problem.
func (p Problem) HTTPStatus() int {
return p.Status
}
// Error satisfies the error interface for Problem by returning the title.
func (p Problem) Error() string {
return p.Title
}
// NewResponder returns a function that can be used to reply to HTTP requests
// with errors.
// If the error is a Problem or has an HTTPStatus method returning an int, its
// status code is used (or 500 if no status code was specified).
// If the HTTPStatus method returns 0, no status code is written and the user
// must write one elsewhere.
// If the error value is nil, a 200 is returned.
func NewResponder() func(http.ResponseWriter, *http.Request, error) error {
return func(w http.ResponseWriter, req *http.Request, err error) error {
if req.Method != "HEAD" {
w.Header().Set("Content-Type", "application/json")
}
// TODO: We should think up a good name for this interface and expose it
// somewhere.
status, ok := err.(interface {
HTTPStatus() int
})
switch {
case ok && status.HTTPStatus() == -1:
case ok && status.HTTPStatus() > 0:
w.WriteHeader(status.HTTPStatus())
case err == nil:
w.WriteHeader(200)
return nil
default:
w.WriteHeader(http.StatusInternalServerError)
}
if req.Method != "HEAD" {
err = json.NewEncoder(w).Encode(err)
if err != nil {
return fmt.Errorf("error encoding JSON error: %q", err)
}
}
return nil
}
}