github.com/gotstago/buffalo@v0.9.5/default_context.go (about) 1 package buffalo 2 3 import ( 4 "bytes" 5 "context" 6 "fmt" 7 "io" 8 "net/http" 9 "net/url" 10 "sort" 11 "strings" 12 "time" 13 14 "github.com/gobuffalo/buffalo/binding" 15 "github.com/gobuffalo/buffalo/render" 16 "github.com/gorilla/websocket" 17 "github.com/pkg/errors" 18 ) 19 20 // assert that DefaultContext is implementing Context 21 var _ Context = &DefaultContext{} 22 var _ context.Context = &DefaultContext{} 23 24 // DefaultContext is, as its name implies, a default 25 // implementation of the Context interface. 26 type DefaultContext struct { 27 context.Context 28 response http.ResponseWriter 29 request *http.Request 30 params url.Values 31 logger Logger 32 session *Session 33 contentType string 34 data map[string]interface{} 35 flash *Flash 36 } 37 38 // Response returns the original Response for the request. 39 func (d *DefaultContext) Response() http.ResponseWriter { 40 return d.response 41 } 42 43 // Request returns the original Request. 44 func (d *DefaultContext) Request() *http.Request { 45 return d.request 46 } 47 48 // Params returns all of the parameters for the request, 49 // including both named params and query string parameters. 50 func (d *DefaultContext) Params() ParamValues { 51 return d.params 52 } 53 54 // Logger returns the Logger for this context. 55 func (d *DefaultContext) Logger() Logger { 56 return d.logger 57 } 58 59 // Param returns a param, either named or query string, 60 // based on the key. 61 func (d *DefaultContext) Param(key string) string { 62 return d.Params().Get(key) 63 } 64 65 // Set a value onto the Context. Any value set onto the Context 66 // will be automatically available in templates. 67 func (d *DefaultContext) Set(key string, value interface{}) { 68 d.data[key] = value 69 } 70 71 // Value that has previously stored on the context. 72 func (d *DefaultContext) Value(key interface{}) interface{} { 73 if k, ok := key.(string); ok { 74 if v, ok := d.data[k]; ok { 75 return v 76 } 77 } 78 return d.Context.Value(key) 79 } 80 81 // Session for the associated Request. 82 func (d *DefaultContext) Session() *Session { 83 return d.session 84 } 85 86 // Cookies for the associated request and response. 87 func (d *DefaultContext) Cookies() *Cookies { 88 return &Cookies{d.request, d.response} 89 } 90 91 // Flash messages for the associated Request. 92 func (d *DefaultContext) Flash() *Flash { 93 return d.flash 94 } 95 96 // Render a status code and render.Renderer to the associated Response. 97 // The request parameters will be made available to the render.Renderer 98 // "{{.params}}". Any values set onto the Context will also automatically 99 // be made available to the render.Renderer. To render "no content" pass 100 // in a nil render.Renderer. 101 func (d *DefaultContext) Render(status int, rr render.Renderer) error { 102 start := time.Now() 103 defer func() { 104 d.LogField("render", time.Since(start)) 105 }() 106 if rr != nil { 107 data := d.data 108 pp := map[string]string{} 109 for k, v := range d.params { 110 pp[k] = v[0] 111 } 112 data["params"] = pp 113 data["flash"] = d.Flash().data 114 data["session"] = d.Session() 115 data["request"] = d.Request() 116 bb := &bytes.Buffer{} 117 118 err := rr.Render(bb, data) 119 if err != nil { 120 return HTTPError{Status: 500, Cause: errors.WithStack(err)} 121 } 122 123 if d.Session() != nil { 124 d.Flash().Clear() 125 d.Flash().persist(d.Session()) 126 } 127 128 d.Response().Header().Set("Content-Type", rr.ContentType()) 129 d.Response().WriteHeader(status) 130 _, err = io.Copy(d.Response(), bb) 131 if err != nil { 132 return HTTPError{Status: 500, Cause: errors.WithStack(err)} 133 } 134 135 return nil 136 } 137 d.Response().WriteHeader(status) 138 return nil 139 } 140 141 // Bind the interface to the request.Body. The type of binding 142 // is dependent on the "Content-Type" for the request. If the type 143 // is "application/json" it will use "json.NewDecoder". If the type 144 // is "application/xml" it will use "xml.NewDecoder". See the 145 // github.com/gobuffalo/buffalo/binding package for more details. 146 func (d *DefaultContext) Bind(value interface{}) error { 147 return binding.Exec(d.Request(), value) 148 } 149 150 // LogField adds the key/value pair onto the Logger to be printed out 151 // as part of the request logging. This allows you to easily add things 152 // like metrics (think DB times) to your request. 153 func (d *DefaultContext) LogField(key string, value interface{}) { 154 d.logger = d.logger.WithField(key, value) 155 } 156 157 // LogFields adds the key/value pairs onto the Logger to be printed out 158 // as part of the request logging. This allows you to easily add things 159 // like metrics (think DB times) to your request. 160 func (d *DefaultContext) LogFields(values map[string]interface{}) { 161 d.logger = d.logger.WithFields(values) 162 } 163 164 func (d *DefaultContext) Error(status int, err error) error { 165 return HTTPError{Status: status, Cause: errors.WithStack(err)} 166 } 167 168 // Websocket returns an upgraded github.com/gorilla/websocket.Conn 169 // that can then be used to work with websockets easily. 170 func (d *DefaultContext) Websocket() (*websocket.Conn, error) { 171 return defaultUpgrader.Upgrade(d.Response(), d.Request(), nil) 172 } 173 174 // Redirect a request with the given status to the given URL. 175 func (d *DefaultContext) Redirect(status int, url string, args ...interface{}) error { 176 d.Flash().persist(d.Session()) 177 178 if len(args) > 0 { 179 url = fmt.Sprintf(url, args...) 180 } 181 http.Redirect(d.Response(), d.Request(), url, status) 182 return nil 183 } 184 185 // Data contains all the values set through Get/Set. 186 func (d *DefaultContext) Data() map[string]interface{} { 187 return d.data 188 } 189 190 func (d *DefaultContext) String() string { 191 bb := make([]string, 0, len(d.data)) 192 193 for k, v := range d.data { 194 if _, ok := v.(RouteHelperFunc); !ok { 195 bb = append(bb, fmt.Sprintf("%s: %s", k, v)) 196 } 197 } 198 sort.Strings(bb) 199 return strings.Join(bb, "\n\n") 200 } 201 202 var defaultUpgrader = websocket.Upgrader{ 203 ReadBufferSize: 1024, 204 WriteBufferSize: 1024, 205 CheckOrigin: func(r *http.Request) bool { return true }, 206 }