-
Notifications
You must be signed in to change notification settings - Fork 4
/
context.go
159 lines (141 loc) · 3.72 KB
/
context.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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package webapi
import (
"errors"
"fmt"
"io/ioutil"
"net/http"
"reflect"
"strconv"
)
var (
//these types can be marshaled to []byte directlty
marshalableKinds = map[reflect.Kind]bool{
reflect.Struct: true,
reflect.Map: true,
reflect.Slice: true,
reflect.Array: true,
}
)
type (
//Middleware Middleware
Middleware interface {
Invoke(ctx *Context, next HTTPHandler)
}
//HTTPHandler Public HTTP Handler
HTTPHandler func(*Context)
httpHandler func(*Context, ...string)
//Context HTTP Request Context
Context struct {
statuscode int
w http.ResponseWriter
r *http.Request
body []byte
predecessors []Middleware
Deserializer Serializer
Serializer Serializer
BeforeReading func([]byte) []byte
BeforeWriting func(int, []byte) []byte
}
)
//Reply Reply to client with any data which can be marshaled into bytes if not bytes or string
func (ctx *Context) Reply(httpstatus int, obj ...interface{}) (err error) {
var data []byte
if len(obj) > 0 && obj[0] != nil {
if _, isErr := obj[0].(error); isErr {
data = []byte(obj[0].(error).Error())
} else if entity := reflect.Indirect(reflect.ValueOf(obj[0])); entity.IsValid() {
value := entity.Interface()
_, isByte := value.([]byte)
_, isRune := value.([]rune)
if kind := entity.Kind(); !isByte && !isRune && marshalableKinds[kind] {
//serializer is using for reply now.
//use deserializer to handle body data instead.
if ctx.Serializer == nil {
//default is json.
ctx.Serializer = Serializers["application/json"]
}
data, err = ctx.Serializer.Marshal(value)
if len(ctx.w.Header().Get("Content-Type")) == 0 {
ctx.w.Header().Set("Content-Type", ctx.Serializer.ContentType())
}
} else {
switch value.(type) {
case []byte:
data = value.([]byte)
break
case []rune:
data = []byte(string(value.([]rune)))
break
default:
data = []byte(fmt.Sprintf("%v", value))
}
}
}
if err != nil {
return
}
}
return ctx.Write(httpstatus, data)
}
//Write Write to response(only for once)
func (ctx *Context) Write(httpstatus int, data []byte) (err error) {
if ctx.statuscode == 0 {
ctx.statuscode = httpstatus
if ctx.BeforeWriting != nil && len(data) > 0 {
data = ctx.BeforeWriting(ctx.statuscode, data)
}
ctx.w.WriteHeader(httpstatus)
if len(data) > 0 {
_, err = ctx.w.Write(data)
}
} else {
err = errors.New("the last written with " + strconv.Itoa(ctx.statuscode) + " has been submitted")
}
return
}
//Redirect Jump to antoher url
func (ctx *Context) Redirect(addr string, httpstatus ...int) {
if len(httpstatus) == 0 || !(httpstatus[0] > 299 && httpstatus[0] < 400) {
httpstatus = []int{http.StatusTemporaryRedirect}
}
ctx.statuscode = httpstatus[0]
http.Redirect(ctx.w, ctx.r, addr, httpstatus[0])
}
//SetCookies Set cookies
func (ctx *Context) SetCookies(cookies ...*http.Cookie) {
for _, cookie := range cookies {
http.SetCookie(ctx.w, cookie)
}
}
//ResponseHeader Response Header
func (ctx *Context) ResponseHeader() http.Header {
return ctx.w.Header()
}
//Context Get Context
func (ctx *Context) Context() *Context {
return ctx
}
//GetRequest Get Request from Context
func (ctx *Context) GetRequest() *http.Request {
return ctx.r
}
//GetResponseWriter Get ResponseWriter as io.Writer to support stream write
func (ctx *Context) GetResponseWriter() ResponseWriter {
return &responsewriter{
ctx: ctx,
}
}
//Body The Body Bytes from Context
func (ctx *Context) Body() []byte {
if ctx.r.Body != nil && ctx.body == nil {
ctx.body, _ = ioutil.ReadAll(ctx.r.Body)
if ctx.body == nil {
ctx.body = []byte{}
}
}
return ctx.body
}
//StatusCode Context Status Code
func (ctx *Context) StatusCode() int {
return ctx.statuscode
}