/
helpers.go
125 lines (92 loc) · 3.07 KB
/
helpers.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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
package app
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"strconv"
"strings"
)
type envelope map[string]interface{}
type info struct {
basePadding int
lines []string
}
func (i *info) pad(padding int) string {
return strings.Repeat(" ", padding) + "%-" + strconv.Itoa(i.basePadding-padding) + "s"
}
func (i *info) addString(padding int, name, value string) {
i.lines = append(i.lines, fmt.Sprintf(i.pad(padding)+"%s", name+":", value))
}
func (i *info) addBool(padding int, name string, value bool) {
i.lines = append(i.lines, fmt.Sprintf(i.pad(padding)+"%t", name+":", value))
}
func (i *info) addInt(padding int, name string, value int) {
i.lines = append(i.lines, fmt.Sprintf(i.pad(padding)+"%d", name+":", value))
}
func (i *info) getLines() string {
return strings.Join(i.lines, "\n")
}
func (app *Application) writeJSON(w http.ResponseWriter, r *http.Request, data interface{}) ([]byte, error) {
js, err := json.Marshal(data)
if err != nil {
return nil, err
}
js = append(js, '\n')
w.Header().Set("Content-Type", "application/json")
return js, nil
}
func (app *Application) readJSON(w http.ResponseWriter, r *http.Request, destination interface{}) error {
decoder := json.NewDecoder(r.Body)
decoder.DisallowUnknownFields()
err := decoder.Decode(destination)
if err != nil {
var syntaxError *json.SyntaxError
var unmarshallTypeError *json.UnmarshalTypeError
var invalidUnmarshallError *json.InvalidUnmarshalError
switch {
case errors.As(err, &syntaxError):
return fmt.Errorf("body contains badly-formed JSON (at character %d)", syntaxError.Offset)
case errors.Is(err, io.ErrUnexpectedEOF):
return errors.New("body contains badly-formed JSON")
case errors.As(err, &unmarshallTypeError):
if unmarshallTypeError.Field != "" {
return fmt.Errorf("body contains incorrect JSON type for field %q", unmarshallTypeError.Field)
}
return fmt.Errorf("body contains incorrect JSON type (at character %d)", unmarshallTypeError.Offset)
case errors.Is(err, io.EOF):
return errors.New("body must not be empty")
case errors.As(err, &invalidUnmarshallError):
panic(err)
default:
return err
}
}
err = decoder.Decode(&struct{}{})
if err != io.EOF {
return errors.New("body must only contain a single JSON value")
}
return nil
}
func (app *Application) errorResponse(w http.ResponseWriter, r *http.Request, status int, message string) {
response, err := app.writeJSON(w, r, envelope{"error": message})
if err != nil {
app.Logger.LogError(err)
w.WriteHeader(http.StatusInternalServerError)
}
w.WriteHeader(status)
_, err = w.Write(response)
if err != nil {
app.Logger.LogError(err)
w.WriteHeader(http.StatusInternalServerError)
}
}
func (app *Application) serverErrorResponse(w http.ResponseWriter, r *http.Request, err error) {
app.Logger.LogError(err)
app.errorResponse(w, r, http.StatusInternalServerError, err.Error())
}
func (app *Application) rateLimitExceededResponse(w http.ResponseWriter, r *http.Request) {
message := "rate limit exceeded"
app.errorResponse(w, r, http.StatusTooManyRequests, message)
}