-
Notifications
You must be signed in to change notification settings - Fork 1
/
contracts_interfaces.go
138 lines (121 loc) · 5.8 KB
/
contracts_interfaces.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
127
128
129
130
131
132
133
134
135
136
137
138
package shuttle
import (
"context"
"errors"
"io"
"net/http"
)
// InputModel represents user input from the HTTP request. Generally speaking, each type represents an operation,
// intention, or instruction. Each intention or operation should be a separate, well-named structure. By design in this
// library, each instance will be reusable and long lived.
//
// As a best practice or design note and to assert application correctness, the fields of any given InputModel should be
// explicitly initialized and populated using garbage or junk values to ensure they are properly Reset.
type InputModel interface {
// Reset clears the contents of the instance and prepares it for the next use.
Reset()
// Bind maps the values on the HTTP request provided to fields on the instance. If there are any problems and error
// is returned which can then be rendered on the response using the configured callback.
Bind(*http.Request) error
// Validate ensures that the instance has all the necessary fields appropriately populated. The slice of errors
// provided is a pre-allocated buffer in which to place any errors encountered during validation. It returns the
// number of errors placed into the buffer provided.
Validate([]error) int
}
// Reader provides the ability to read values from the incoming HTTP request and to either manipulate the associated
// InputModel in some fashion or to otherwise short-circuit the request pipeline by returning a result to be rendered
// the caller's HTTP response stream. If a nil (meaning successful) result is returned, then processing continues.
type Reader interface {
Read(InputModel, *http.Request) any
}
// ResultContainer sets the content on the underlying instance.
type ResultContainer interface {
SetContent(any)
Result() any
}
// Processor represents the mechanism used to carry out the desired instruction or user-provided intention. The second
// value is deliberately left opaque to reduce library dependencies and to encourage proper type discovery of the
// InputModel provided.
//
// Depending upon how the processor is implemented (e.g. stateless and shared vs stateful between unique requests), the
// result returned can be allocated in the Processor instance and returned, or it can be a stateful field somewhere in
// the Processor's object graph.
//
// For Processors that are shared and where each response pathway is stored as a field in the Processor's object graph,
// it may be helpful to follow the InputModel pattern of initializing with garbage or junk values. This will help to
// ensure that all fields are appropriately cleared and overwritten between requests.
//
// The value returned by the processor may be a primitive type, a TextResult, BinaryResult, StreamResult,
// SerializeResult or the aforementioned types using a pointer. If the value returned implements the http.Handler
// interface, that method will be invoked to render the result directly using the underlying http.Request
// and http.ResponseWriter. If the value returned is not one of the aforementioned types, it will be serialized using
// either the requested HTTP Accept type or it will use the default serializer configured, if any.
type Processor interface {
Process(context.Context, any) any
}
// Writer is responsible to render to result provided to the associated response stream.
type Writer interface {
Write(http.ResponseWriter, *http.Request, any)
}
// DeserializeBody is an interface that is optionally implemented by a given InputModel and is used to provide the
// target instance into which the HTTP request body will be deserialized.
type DeserializeBody interface {
Body() any
}
// Deserializer instances provide the ability to transform an opaque byte stream into an instance of a structure.
type Deserializer interface {
// Deserialize renders the decodes the source stream into the instance provided. If there are any problems, an error is returned.
Deserialize(any, io.Reader) error
}
// Serializer instances provide the ability to transform an instance of a structure into a byte stream.
type Serializer interface {
// Serialize renders the instance provided to the io.Writer. If there are any problems, an error is returned.
Serialize(io.Writer, any) error
// ContentType returns HTTP Content-Type header that will be used when writing to the HTTP response.
ContentType() string
}
type Monitor interface {
HandlerCreated()
RequestReceived()
NotAcceptable()
UnsupportedMediaType()
Deserialize()
DeserializeFailed()
ParseForm()
ParseFormFailed(error)
Bind()
BindFailed(error)
Validate()
ValidateFailed([]error)
TextResult()
BinaryResult()
StreamResult()
SerializeResult()
NativeResult()
SerializeFailed()
ResponseStatus(int)
ResponseFailed(error)
}
var (
// ErrDeserializationFailure indicates that there was some kind of problem deserializing the request stream.
ErrDeserializationFailure = errors.New("failed to deserialize the stream into the instance provided")
// ErrSerializationFailure indicates that there was some kind of problem serializing the structure to the response stream.
ErrSerializationFailure = errors.New("failed to serialize the instance into the stream provided")
)
const (
mimeTypeApplicationJSON = "application/json"
mimeTypeApplicationJSONUTF8 = mimeTypeApplicationJSON + characterSetUTF8
mimeTypeApplicationXML = "application/xml"
mimeTypeApplicationXMLUTF8 = mimeTypeApplicationXML + characterSetUTF8
mimeTypeApplicationTextXML = "text/xml"
characterSetUTF8 = "; charset=utf-8"
headerContentType = "Content-Type"
headerContentDisposition = "Content-Disposition"
headerAccept = "Accept"
headerAcceptAnyValue = "*/*"
emptyContentType = ""
)
var (
utf8ByteOrderMark = []byte{239, 187, 191} // http://en.wikipedia.org/wiki/Byte_order_mark
xmlPrefix = append(utf8ByteOrderMark, []byte(`<?xml version="1.0" encoding="utf-8"?>`)...)
)