/
serialize.go
126 lines (101 loc) · 3.08 KB
/
serialize.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
126
/*
Copyright 2014 - 2015 Workiva, LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rest
import (
"encoding/json"
"net/http"
"reflect"
)
const (
status = "status"
reason = "reason"
messages = "messages"
result = "result"
results = "results"
next = "next"
)
// response is a data structure holding the serializable response body for a request and
// HTTP status code. It should be created using NewResponse.
type response struct {
Payload Payload
Status int
}
// ResponseSerializer is responsible for serializing REST responses and sending
// them back to the client.
type ResponseSerializer interface {
// Serialize marshals a response payload into a byte slice to be sent over the wire.
Serialize(Payload) ([]byte, error)
// ContentType returns the MIME type of the response.
ContentType() string
}
// jsonSerializer is an implementation of ResponseSerializer which serializes responses
// as JSON.
type jsonSerializer struct{}
// Serialize marshals a response payload into a JSON byte slice to be sent over the wire.
func (j jsonSerializer) Serialize(p Payload) ([]byte, error) {
return json.Marshal(p)
}
// ContentType returns the JSON MIME type of the response.
func (j jsonSerializer) ContentType() string {
return "application/json"
}
// NewResponse constructs a new response struct containing the payload to send back.
// It will either be a success or error response depending on the RequestContext.
func NewResponse(ctx RequestContext) response {
err := ctx.Error()
if err != nil {
return newErrorResponse(ctx)
}
return newSuccessResponse(ctx)
}
// newSuccessResponse constructs a new response struct containing a resource response.
func newSuccessResponse(ctx RequestContext) response {
r := ctx.Result()
resultKey := result
if r != nil && reflect.TypeOf(r).Kind() == reflect.Slice {
resultKey = results
}
s := ctx.Status()
response := response{Status: s}
if s != http.StatusNoContent {
payload := Payload{
status: s,
reason: http.StatusText(s),
messages: ctx.Messages(),
resultKey: r,
}
if nextURL, err := ctx.NextURL(); err == nil && nextURL != "" {
payload[next] = nextURL
}
response.Payload = payload
}
return response
}
// newErrorResponse constructs a new response struct containing an error message.
func newErrorResponse(ctx RequestContext) response {
err := ctx.Error()
s := http.StatusInternalServerError
if restError, ok := err.(Error); ok {
s = restError.Status()
}
payload := Payload{
status: s,
reason: http.StatusText(s),
messages: ctx.Messages(),
}
response := response{
Payload: payload,
Status: s,
}
return response
}