github.com/gramework/gramework@v1.8.1-0.20231027140105-82555c9057f5/context.go (about) 1 // Copyright 2017-present Kirill Danshin and Gramework contributors 2 // Copyright 2019-present Highload LTD (UK CN: 11893420) 3 // 4 // Licensed under the Apache License, Version 2.0 (the "License"); 5 // you may not use this file except in compliance with the License. 6 // You may obtain a copy of the License at 7 // 8 // http://www.apache.org/licenses/LICENSE-2.0 9 // 10 11 package gramework 12 13 import ( 14 "bytes" 15 "context" 16 "encoding/xml" 17 "fmt" 18 19 "github.com/microcosm-cc/bluemonday" 20 "github.com/pquerna/ffjson/ffjson" 21 22 "github.com/gocarina/gocsv" 23 ) 24 25 // @TODO: add more 26 var ctypes = []string{ 27 jsonCT, 28 xmlCT, 29 csvCT, 30 } 31 32 // ContextFromValue returns gramework.Context from context.Context value from gramework.ContextKey 33 // in a more effective way, than standard eface.(*SomeType). 34 // WARNING: this function may return nil, if ctx has no gramework.Context stored or ctx is nil. 35 // This function will give a warning if you call it with nil context.Context. 36 func ContextFromValue(ctx context.Context) *Context { 37 if ctx == nil { 38 internalLog.Warn("ContextFromValue was called with nil context.Context, returning nil") 39 return nil 40 } 41 42 ctxIface := ctx.Value(ContextKey) 43 gctx, ok := ctxIface.(*Context) 44 if !ok { 45 return nil 46 } 47 48 return gctx 49 } 50 51 // MWKill kills current context and stop any user-defined processing. 52 // This function intented for use in middlewares. 53 func (ctx *Context) MWKill() { 54 ctx.middlewareKilledReq = true 55 } 56 57 // Sanitize returns a sanitized `s`. 58 // It use bluemonday to sanitize given parameter. 59 // 60 // To change sanitizer policy, see (*App).SetSanitizerPolicy. 61 func (ctx *Context) Sanitize(s string) string { 62 return ctx.App.sanitizerPolicy.Sanitize(s) 63 } 64 65 // Sanitizer returns current bluemonday policy. 66 // 67 // To change sanitizer policy, see (*App).SetSanitizerPolicy. 68 // 69 // Context must not update the policy at runtime. Instead, please 70 // use a new policy. 71 func (ctx *Context) Sanitizer() *bluemonday.Policy { 72 return ctx.App.sanitizerPolicy 73 } 74 75 // SubPrefixes returns list of router's prefixes that was created using .Sub() feature 76 func (ctx *Context) SubPrefixes() []string { 77 return ctx.subPrefixes 78 } 79 80 // ContentType returns Content-Type header for current request 81 func (ctx *Context) ContentType() string { 82 return string(ctx.Request.Header.Peek(contentType)) 83 } 84 85 // ToContext returns context.Context with gramework.Context stored 86 // in context values as a pointer (see gramework.ContextKey to receive and use this value). 87 // 88 // By default this func will extend context.Background(), if parentCtx is not provided. 89 func (ctx *Context) ToContext(parentCtx ...context.Context) context.Context { 90 if len(parentCtx) > 0 { 91 return context.WithValue(parentCtx[0], ContextKey, ctx) 92 } 93 94 return context.WithValue(context.Background(), ContextKey, ctx) 95 } 96 97 // RouteArg returns an argument value as a string or empty string 98 func (ctx *Context) RouteArg(argName string) string { 99 v, err := ctx.RouteArgErr(argName) 100 if err != nil { 101 return emptyString 102 } 103 return v 104 } 105 106 // ToCSV encodes csv-encoded value to client 107 func (ctx *Context) ToCSV(v interface{}) ([]byte, error) { 108 return gocsv.MarshalBytes(v) 109 } 110 111 // ToXML encodes xml-encoded value to client 112 func (ctx *Context) ToXML(v interface{}) ([]byte, error) { 113 b := bytes.NewBuffer(nil) 114 err := xml.NewEncoder(b).Encode(v) 115 return b.Bytes(), err 116 } 117 118 // GETKeys returns GET parameters keys (query args) 119 func (ctx *Context) GETKeys() []string { 120 var res []string 121 ctx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) { 122 res = append(res, string(key)) 123 }) 124 return res 125 } 126 127 // GETKeysBytes returns GET parameters keys (query args) as []byte 128 func (ctx *Context) GETKeysBytes() [][]byte { 129 var res [][]byte 130 ctx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) { 131 res = append(res, key) 132 }) 133 return res 134 } 135 136 // GETParams returns GET parameters (query args) 137 func (ctx *Context) GETParams() map[string][]string { 138 res := make(map[string][]string) 139 ctx.Request.URI().QueryArgs().VisitAll(func(key, value []byte) { 140 res[string(key)] = append(res[string(key)], string(value)) 141 }) 142 return res 143 } 144 145 // GETParam returns GET parameter (query arg) by name 146 func (ctx *Context) GETParam(argName string) []string { 147 res := ctx.GETParams() 148 if param, ok := res[argName]; ok { 149 return param 150 } 151 return nil 152 } 153 154 // RouteArgErr returns an argument value as a string or empty string 155 // and ErrArgNotFound if argument was not found 156 func (ctx *Context) RouteArgErr(argName string) (string, error) { 157 i := ctx.UserValue(argName) 158 if i == nil { 159 return emptyString, ErrArgNotFound 160 } 161 switch value := i.(type) { 162 case string: 163 return value, nil 164 default: 165 return fmt.Sprintf(fmtV, i), nil 166 } 167 } 168 169 // ToTLS redirects user to HTTPS scheme 170 func (ctx *Context) ToTLS() { 171 u := ctx.URI() 172 u.SetScheme(https) 173 ctx.Redirect(u.String(), redirectCode) 174 } 175 176 // Forbidden send 403 Forbidden error 177 func (ctx *Context) Forbidden() { 178 ctx.Error(forbidden, forbiddenCode) 179 } 180 181 // ToJSON serializes v and returns the result 182 func (ctx *Context) ToJSON(v interface{}) ([]byte, error) { 183 b := bytes.NewBuffer(nil) 184 enc := ffjson.NewEncoder(b) 185 err := enc.Encode(v) 186 return b.Bytes(), err 187 } 188 189 // UnJSONBytes deserializes JSON request body to given variable pointer or allocates a new one. 190 // Returns resulting data and error. One of them may be nil. 191 func (ctx *Context) UnJSONBytes(b []byte, v ...interface{}) (interface{}, error) { 192 return UnJSONBytes(b, v...) 193 } 194 195 // UnJSON deserializes JSON request body to given variable pointer 196 func (ctx *Context) UnJSON(v interface{}) error { 197 return ffjson.NewDecoder().Decode(ctx.Request.Body(), &v) 198 } 199 200 // UnJSONBytes deserializes JSON request body to given variable pointer or allocates a new one. 201 // Returns resulting data and error. One of them may be nil. 202 func UnJSONBytes(b []byte, v ...interface{}) (interface{}, error) { 203 if len(v) == 0 { 204 var res interface{} 205 err := ffjson.NewDecoder().Decode(b, &res) 206 return res, err 207 } 208 err := ffjson.NewDecoder().Decode(b, &v[0]) 209 return v[0], err 210 } 211 212 func (ctx *Context) jsonErrorLog(v interface{}) { 213 ctx.Err500() 214 if err := ctx.JSON(v); err != nil { 215 ctx.Logger.WithError(err).Error("JSONError err") 216 } 217 } 218 219 // RequestID return request ID for current context's request 220 func (ctx *Context) RequestID() string { 221 return ctx.requestID 222 }