Add API Framework Revel Source Files
[iec.git] / src / foundation / api / revel / controller.go
1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
4
5 package revel
6
7 import (
8         "errors"
9         "fmt"
10         "io"
11         "net/http"
12         "os"
13         "path/filepath"
14         "reflect"
15         "runtime"
16         "strings"
17         "time"
18
19         "github.com/revel/revel/logger"
20         "github.com/revel/revel/session"
21         "github.com/revel/revel/utils"
22 )
23
24 // Controller Revel's controller structure that gets embedded in user defined
25 // controllers
26 type Controller struct {
27         Name          string          // The controller name, e.g. "Application"
28         Type          *ControllerType // A description of the controller type.
29         MethodName    string          // The method name, e.g. "Index"
30         MethodType    *MethodType     // A description of the invoked action type.
31         AppController interface{}     // The controller that was instantiated. embeds revel.Controller
32         Action        string          // The fully qualified action name, e.g. "App.Index"
33         ClientIP      string          // holds IP address of request came from
34
35         Request  *Request
36         Response *Response
37         Result   Result
38
39         Flash      Flash                  // User cookie, cleared after 1 request.
40         Session    session.Session        // Session, stored using the session engine specified
41         Params     *Params                // Parameters from URL and form (including multipart).
42         Args       map[string]interface{} // Per-request scratch space.
43         ViewArgs   map[string]interface{} // Variables passed to the template.
44         Validation *Validation            // Data validation helpers
45         Log        logger.MultiLogger     // Context Logger
46 }
47
48 // The map of controllers, controllers are mapped by using the namespace|controller_name as the key
49 var controllers = make(map[string]*ControllerType)
50 var controllerLog = RevelLog.New("section", "controller")
51
52 // NewController returns new controller instance for Request and Response
53 func NewControllerEmpty() *Controller {
54         return &Controller{Request: NewRequest(nil), Response: NewResponse(nil)}
55 }
56
57 // New controller, creates a new instance wrapping the request and response in it
58 func NewController(context ServerContext) *Controller {
59         c := NewControllerEmpty()
60         c.SetController(context)
61         return c
62 }
63
64 // Sets the request and the response for the controller
65 func (c *Controller) SetController(context ServerContext) {
66
67         c.Request.SetRequest(context.GetRequest())
68         c.Response.SetResponse(context.GetResponse())
69         c.Request.controller = c
70         c.Params = new(Params)
71         c.Args = map[string]interface{}{}
72         c.ViewArgs = map[string]interface{}{
73                 "RunMode": RunMode,
74                 "DevMode": DevMode,
75         }
76
77 }
78 func (c *Controller) Destroy() {
79         // When the instantiated controller gets injected
80         // It inherits this method, so we need to
81         // check to see if the controller is nil before performing
82         // any actions
83         if c == nil {
84                 return
85         }
86         if c.AppController != nil {
87                 c.resetAppControllerFields()
88                 // Return this instance to the pool
89                 appController := c.AppController
90                 c.AppController = nil
91                 if RevelConfig.Controller.Reuse {
92                         RevelConfig.Controller.CachedMap[c.Name].Push(appController)
93                 }
94                 c.AppController = nil
95         }
96
97         c.Request.Destroy()
98         c.Response.Destroy()
99         c.Params = nil
100         c.Args = nil
101         c.ViewArgs = nil
102         c.Name = ""
103         c.Type = nil
104         c.MethodName = ""
105         c.MethodType = nil
106         c.Action = ""
107         c.ClientIP = ""
108         c.Result = nil
109         c.Flash = Flash{}
110         c.Session = session.NewSession()
111         c.Params = nil
112         c.Validation = nil
113         c.Log = nil
114 }
115
116 // FlashParams serializes the contents of Controller.Params to the Flash
117 // cookie.
118 func (c *Controller) FlashParams() {
119         for key, vals := range c.Params.Values {
120                 c.Flash.Out[key] = strings.Join(vals, ",")
121         }
122 }
123
124 func (c *Controller) SetCookie(cookie *http.Cookie) {
125         c.Response.Out.internalHeader.SetCookie(cookie.String())
126 }
127
128 type ErrorCoder interface {
129         HTTPCode() int
130 }
131
132 func (c *Controller) RenderError(err error) Result {
133         if coder, ok := err.(ErrorCoder); ok {
134                 c.setStatusIfNil(coder.HTTPCode())
135         } else {
136                 c.setStatusIfNil(http.StatusInternalServerError)
137         }
138
139         return ErrorResult{c.ViewArgs, err}
140 }
141
142 func (c *Controller) setStatusIfNil(status int) {
143         if c.Response.Status == 0 {
144                 c.Response.Status = status
145         }
146 }
147
148 // Render a template corresponding to the calling Controller method.
149 // Arguments will be added to c.ViewArgs prior to rendering the template.
150 // They are keyed on their local identifier.
151 //
152 // For example:
153 //
154 //     func (c Users) ShowUser(id int) revel.Result {
155 //       user := loadUser(id)
156 //       return c.Render(user)
157 //     }
158 //
159 // This action will render views/Users/ShowUser.html, passing in an extra
160 // key-value "user": (User).
161 //
162 // This is the slower magical version which uses the runtime
163 // to determine
164 // 1) Set c.ViewArgs to the arguments passed into this function
165 // 2) How to call the RenderTemplate by building the following line
166 // c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
167 //
168 // If you want your code to run faster it is recommended you add the template values directly
169 // to the c.ViewArgs and call c.RenderTemplate directly
170 func (c *Controller) Render(extraViewArgs ...interface{}) Result {
171         c.setStatusIfNil(http.StatusOK)
172
173         // Get the calling function line number.
174         _, _, line, ok := runtime.Caller(1)
175         if !ok {
176                 controllerLog.Error("Render: Failed to get Caller information")
177         }
178
179         // Get the extra ViewArgs passed in.
180         if renderArgNames, ok := c.MethodType.RenderArgNames[line]; ok {
181                 if len(renderArgNames) == len(extraViewArgs) {
182                         for i, extraRenderArg := range extraViewArgs {
183                                 c.ViewArgs[renderArgNames[i]] = extraRenderArg
184                         }
185                 } else {
186                         controllerLog.Error(fmt.Sprint(len(renderArgNames), "RenderArg names found for",
187                                 len(extraViewArgs), "extra ViewArgs"))
188                 }
189         } else {
190                 controllerLog.Error(fmt.Sprint("No RenderArg names found for Render call on line", line,
191                         "(Action", c.Action, ")"),"stack",logger.NewCallStack())
192         }
193
194         return c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
195 }
196
197 // RenderTemplate method does less magical way to render a template.
198 // Renders the given template, using the current ViewArgs.
199 func (c *Controller) RenderTemplate(templatePath string) Result {
200         c.setStatusIfNil(http.StatusOK)
201
202         // Get the Template.
203         lang, _ := c.ViewArgs[CurrentLocaleViewArg].(string)
204         template, err := MainTemplateLoader.TemplateLang(templatePath, lang)
205         if err != nil {
206                 return c.RenderError(err)
207         }
208
209         return &RenderTemplateResult{
210                 Template: template,
211                 ViewArgs: c.ViewArgs,
212         }
213 }
214
215 // TemplateOutput returns the result of the template rendered using the controllers ViewArgs.
216 func (c *Controller) TemplateOutput(templatePath string) (data []byte, err error) {
217         return TemplateOutputArgs(templatePath, c.ViewArgs)
218 }
219
220 // RenderJSON uses encoding/json.Marshal to return JSON to the client.
221 func (c *Controller) RenderJSON(o interface{}) Result {
222         c.setStatusIfNil(http.StatusOK)
223
224         return RenderJSONResult{o, ""}
225 }
226
227 // RenderJSONP renders JSONP result using encoding/json.Marshal
228 func (c *Controller) RenderJSONP(callback string, o interface{}) Result {
229         c.setStatusIfNil(http.StatusOK)
230
231         return RenderJSONResult{o, callback}
232 }
233
234 // RenderXML uses encoding/xml.Marshal to return XML to the client.
235 func (c *Controller) RenderXML(o interface{}) Result {
236         c.setStatusIfNil(http.StatusOK)
237
238         return RenderXMLResult{o}
239 }
240
241 // RenderText renders plaintext in response, printf style.
242 func (c *Controller) RenderText(text string, objs ...interface{}) Result {
243         c.setStatusIfNil(http.StatusOK)
244
245         finalText := text
246         if len(objs) > 0 {
247                 finalText = fmt.Sprintf(text, objs...)
248         }
249         return &RenderTextResult{finalText}
250 }
251
252 // RenderHTML renders html in response
253 func (c *Controller) RenderHTML(html string) Result {
254         c.setStatusIfNil(http.StatusOK)
255
256         return &RenderHTMLResult{html}
257 }
258
259 // Todo returns an HTTP 501 Not Implemented "todo" indicating that the
260 // action isn't done yet.
261 func (c *Controller) Todo() Result {
262         c.Response.Status = http.StatusNotImplemented
263         controllerLog.Debug("Todo: Not implemented function", "action", c.Action)
264         return c.RenderError(&Error{
265                 Title:       "TODO",
266                 Description: "This action is not implemented",
267         })
268 }
269
270 // NotFound returns an HTTP 404 Not Found response whose body is the
271 // formatted string of msg and objs.
272 func (c *Controller) NotFound(msg string, objs ...interface{}) Result {
273         finalText := msg
274         if len(objs) > 0 {
275                 finalText = fmt.Sprintf(msg, objs...)
276         }
277         c.Response.Status = http.StatusNotFound
278         return c.RenderError(&Error{
279                 Title:       "Not Found",
280                 Description: finalText,
281         })
282 }
283
284 // Forbidden returns an HTTP 403 Forbidden response whose body is the
285 // formatted string of msg and objs.
286 func (c *Controller) Forbidden(msg string, objs ...interface{}) Result {
287         finalText := msg
288         if len(objs) > 0 {
289                 finalText = fmt.Sprintf(msg, objs...)
290         }
291         c.Response.Status = http.StatusForbidden
292         return c.RenderError(&Error{
293                 Title:       "Forbidden",
294                 Description: finalText,
295         })
296 }
297
298 // RenderFileName returns a file indicated by the path as provided via the filename.
299 // It can be either displayed inline or downloaded as an attachment.
300 // The name and size are taken from the file info.
301 func (c *Controller) RenderFileName(filename string, delivery ContentDisposition) Result {
302         f, err := os.Open(filename)
303         if err != nil {
304                 c.Log.Errorf("Cant open file: %v", err)
305                 return c.RenderError(err)
306         }
307         return c.RenderFile(f, delivery)
308 }
309
310 // RenderFile returns a file, either displayed inline or downloaded
311 // as an attachment. The name and size are taken from the file info.
312 func (c *Controller) RenderFile(file *os.File, delivery ContentDisposition) Result {
313         c.setStatusIfNil(http.StatusOK)
314
315         var (
316                 modtime       = time.Now()
317                 fileInfo, err = file.Stat()
318         )
319         if err != nil {
320                 controllerLog.Error("RenderFile: error", "error", err)
321         }
322         if fileInfo != nil {
323                 modtime = fileInfo.ModTime()
324         }
325         return c.RenderBinary(file, filepath.Base(file.Name()), delivery, modtime)
326 }
327
328 // RenderBinary is like RenderFile() except that it instead of a file on disk,
329 // it renders data from memory (which could be a file that has not been written,
330 // the output from some function, or bytes streamed from somewhere else, as long
331 // it implements io.Reader).  When called directly on something generated or
332 // streamed, modtime should mostly likely be time.Now().
333 func (c *Controller) RenderBinary(memfile io.Reader, filename string, delivery ContentDisposition, modtime time.Time) Result {
334         c.setStatusIfNil(http.StatusOK)
335
336         return &BinaryResult{
337                 Reader:   memfile,
338                 Name:     filename,
339                 Delivery: delivery,
340                 Length:   -1, // http.ServeContent gets the length itself unless memfile is a stream.
341                 ModTime:  modtime,
342         }
343 }
344
345 // Redirect to an action or to a URL.
346 //   c.Redirect(Controller.Action)
347 //   c.Redirect("/controller/action")
348 //   c.Redirect("/controller/%d/action", id)
349 func (c *Controller) Redirect(val interface{}, args ...interface{}) Result {
350         c.setStatusIfNil(http.StatusFound)
351
352         if url, ok := val.(string); ok {
353                 if len(args) == 0 {
354                         return &RedirectToURLResult{url}
355                 }
356                 return &RedirectToURLResult{fmt.Sprintf(url, args...)}
357         }
358         return &RedirectToActionResult{val, args}
359 }
360
361 // This stats returns some interesting stats based on what is cached in memory
362 // and what is available directly
363 func (c *Controller) Stats() map[string]interface{} {
364         result := CurrentEngine.Stats()
365         if RevelConfig.Controller.Reuse {
366                 result["revel-controllers"] = RevelConfig.Controller.Stack.String()
367                 for key, appStack := range RevelConfig.Controller.CachedMap {
368                         result["app-" + key] = appStack.String()
369                 }
370         }
371         return result
372 }
373
374 // Message performs a lookup for the given message name using the given
375 // arguments using the current language defined for this controller.
376 //
377 // The current language is set by the i18n plugin.
378 func (c *Controller) Message(message string, args ...interface{}) string {
379         return MessageFunc(c.Request.Locale, message, args...)
380 }
381
382 // SetAction sets the action that is being invoked in the current request.
383 // It sets the following properties: Name, Action, Type, MethodType
384 func (c *Controller) SetAction(controllerName, methodName string) error {
385
386         return c.SetTypeAction(controllerName, methodName, nil)
387 }
388
389 // SetAction sets the assigns the Controller type, sets the action and initializes the controller
390 func (c *Controller) SetTypeAction(controllerName, methodName string, typeOfController *ControllerType) error {
391
392         // Look up the controller and method types.
393         if typeOfController == nil {
394                 if c.Type = ControllerTypeByName(controllerName, anyModule); c.Type == nil {
395                         return errors.New("revel/controller: failed to find controller " + controllerName)
396                 }
397         } else {
398                 c.Type = typeOfController
399         }
400
401         // Note method name is case insensitive search
402         if c.MethodType = c.Type.Method(methodName); c.MethodType == nil {
403                 return errors.New("revel/controller: failed to find action " + controllerName + "." + methodName)
404         }
405
406         c.Name, c.MethodName = c.Type.Type.Name(), c.MethodType.Name
407         c.Action = c.Name + "." + c.MethodName
408
409         // Update Logger with controller and namespace
410         if c.Log != nil {
411                 c.Log = c.Log.New("action", c.Action, "namespace", c.Type.Namespace)
412         }
413
414         if RevelConfig.Controller.Reuse {
415                 if _, ok := RevelConfig.Controller.CachedMap[c.Name]; !ok {
416                         // Create a new stack for this controller
417                         localType := c.Type.Type
418                         RevelConfig.Controller.CachedMap[c.Name] = utils.NewStackLock(
419                                 RevelConfig.Controller.CachedStackSize,
420                                 RevelConfig.Controller.CachedStackMaxSize,
421                                 func() interface{} {
422                                         return reflect.New(localType).Interface()
423                                 })
424                 }
425                 // Instantiate the controller.
426                 c.AppController = RevelConfig.Controller.CachedMap[c.Name].Pop()
427         } else {
428                 c.AppController = reflect.New(c.Type.Type).Interface()
429         }
430         c.setAppControllerFields()
431
432         return nil
433 }
434
435 func ControllerTypeByName(controllerName string, moduleSource *Module) (c *ControllerType) {
436         var found bool
437         if c, found = controllers[controllerName]; !found {
438                 // Backup, passed in controllerName should be in lower case, but may not be
439                 if c, found = controllers[strings.ToLower(controllerName)]; !found {
440                         controllerLog.Debug("ControllerTypeByName: Cannot find controller in controllers map ", "controller", controllerName)
441                         // Search for the controller by name
442                         for _, cType := range controllers {
443                                 testControllerName := strings.ToLower(cType.Type.Name())
444                                 if testControllerName == strings.ToLower(controllerName) && (cType.ModuleSource == moduleSource || moduleSource == anyModule) {
445                                         controllerLog.Warn("ControllerTypeByName: Matched empty namespace controller ", "controller", controllerName, "namespace", cType.ModuleSource.Name)
446                                         c = cType
447                                         found = true
448                                         break
449                                 }
450                         }
451                 }
452         }
453         return
454 }
455
456 // Injects this instance (c) into the AppController instance
457 func (c *Controller) setAppControllerFields() {
458         appController := reflect.ValueOf(c.AppController).Elem()
459         cValue := reflect.ValueOf(c)
460         for _, index := range c.Type.ControllerIndexes {
461                 appController.FieldByIndex(index).Set(cValue)
462         }
463 }
464
465 // Removes this instance (c) from the AppController instance
466 func (c *Controller) resetAppControllerFields() {
467         appController := reflect.ValueOf(c.AppController).Elem()
468         // Zero out controller
469         for _, index := range c.Type.ControllerIndexes {
470                 appController.FieldByIndex(index).Set(reflect.Zero(reflect.TypeOf(c.AppController).Elem().FieldByIndex(index).Type))
471         }
472 }
473
474 func findControllers(appControllerType reflect.Type) (indexes [][]int) {
475         // It might be a multi-level embedding. To find the controllers, we follow
476         // every anonymous field, using breadth-first search.
477         type nodeType struct {
478                 val   reflect.Value
479                 index []int
480         }
481         appControllerPtr := reflect.New(appControllerType)
482         queue := []nodeType{{appControllerPtr, []int{}}}
483         for len(queue) > 0 {
484                 // Get the next value and de-reference it if necessary.
485                 var (
486                         node     = queue[0]
487                         elem     = node.val
488                         elemType = elem.Type()
489                 )
490                 if elemType.Kind() == reflect.Ptr {
491                         elem = elem.Elem()
492                         elemType = elem.Type()
493                 }
494                 queue = queue[1:]
495
496                 // #944 if the type's Kind is not `Struct` move on,
497                 // otherwise `elem.NumField()` will panic
498                 if elemType.Kind() != reflect.Struct {
499                         continue
500                 }
501
502                 // Look at all the struct fields.
503                 for i := 0; i < elem.NumField(); i++ {
504                         // If this is not an anonymous field, skip it.
505                         structField := elemType.Field(i)
506                         if !structField.Anonymous {
507                                 continue
508                         }
509
510                         fieldValue := elem.Field(i)
511                         fieldType := structField.Type
512
513                         // If it's a Controller, record the field indexes to get here.
514                         if fieldType == controllerPtrType {
515                                 indexes = append(indexes, append(node.index, i))
516                                 continue
517                         }
518
519                         queue = append(queue,
520                                 nodeType{fieldValue, append(append([]int{}, node.index...), i)})
521                 }
522         }
523         return
524 }
525
526 // RegisterController registers a Controller and its Methods with Revel.
527 func RegisterController(c interface{}, methods []*MethodType) {
528         // De-star the controller type
529         // (e.g. given TypeOf((*Application)(nil)), want TypeOf(Application))
530         elem := reflect.TypeOf(c).Elem()
531
532         // De-star all of the method arg types too.
533         for _, m := range methods {
534                 m.lowerName = strings.ToLower(m.Name)
535                 for _, arg := range m.Args {
536                         arg.Type = arg.Type.Elem()
537                 }
538         }
539
540         // Fetch module for controller, if none found controller must be part of the app
541         controllerModule := ModuleFromPath(elem.PkgPath(), true)
542
543         controllerType := AddControllerType(controllerModule, elem, methods)
544
545         controllerLog.Debug("RegisterController:Registered controller", "controller", controllerType.Name())
546 }