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.
19 "github.com/revel/revel/logger"
20 "github.com/revel/revel/session"
21 "github.com/revel/revel/utils"
24 // Controller Revel's controller structure that gets embedded in user defined
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
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
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")
52 // NewController returns new controller instance for Request and Response
53 func NewControllerEmpty() *Controller {
54 return &Controller{Request: NewRequest(nil), Response: NewResponse(nil)}
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)
64 // Sets the request and the response for the controller
65 func (c *Controller) SetController(context ServerContext) {
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{}{
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
86 if c.AppController != nil {
87 c.resetAppControllerFields()
88 // Return this instance to the pool
89 appController := c.AppController
91 if RevelConfig.Controller.Reuse {
92 RevelConfig.Controller.CachedMap[c.Name].Push(appController)
110 c.Session = session.NewSession()
116 // FlashParams serializes the contents of Controller.Params to the Flash
118 func (c *Controller) FlashParams() {
119 for key, vals := range c.Params.Values {
120 c.Flash.Out[key] = strings.Join(vals, ",")
124 func (c *Controller) SetCookie(cookie *http.Cookie) {
125 c.Response.Out.internalHeader.SetCookie(cookie.String())
128 type ErrorCoder interface {
132 func (c *Controller) RenderError(err error) Result {
133 if coder, ok := err.(ErrorCoder); ok {
134 c.setStatusIfNil(coder.HTTPCode())
136 c.setStatusIfNil(http.StatusInternalServerError)
139 return ErrorResult{c.ViewArgs, err}
142 func (c *Controller) setStatusIfNil(status int) {
143 if c.Response.Status == 0 {
144 c.Response.Status = status
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.
154 // func (c Users) ShowUser(id int) revel.Result {
155 // user := loadUser(id)
156 // return c.Render(user)
159 // This action will render views/Users/ShowUser.html, passing in an extra
160 // key-value "user": (User).
162 // This is the slower magical version which uses the runtime
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)
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)
173 // Get the calling function line number.
174 _, _, line, ok := runtime.Caller(1)
176 controllerLog.Error("Render: Failed to get Caller information")
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
186 controllerLog.Error(fmt.Sprint(len(renderArgNames), "RenderArg names found for",
187 len(extraViewArgs), "extra ViewArgs"))
190 controllerLog.Error(fmt.Sprint("No RenderArg names found for Render call on line", line,
191 "(Action", c.Action, ")"),"stack",logger.NewCallStack())
194 return c.RenderTemplate(c.Name + "/" + c.MethodType.Name + "." + c.Request.Format)
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)
203 lang, _ := c.ViewArgs[CurrentLocaleViewArg].(string)
204 template, err := MainTemplateLoader.TemplateLang(templatePath, lang)
206 return c.RenderError(err)
209 return &RenderTemplateResult{
211 ViewArgs: c.ViewArgs,
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)
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)
224 return RenderJSONResult{o, ""}
227 // RenderJSONP renders JSONP result using encoding/json.Marshal
228 func (c *Controller) RenderJSONP(callback string, o interface{}) Result {
229 c.setStatusIfNil(http.StatusOK)
231 return RenderJSONResult{o, callback}
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)
238 return RenderXMLResult{o}
241 // RenderText renders plaintext in response, printf style.
242 func (c *Controller) RenderText(text string, objs ...interface{}) Result {
243 c.setStatusIfNil(http.StatusOK)
247 finalText = fmt.Sprintf(text, objs...)
249 return &RenderTextResult{finalText}
252 // RenderHTML renders html in response
253 func (c *Controller) RenderHTML(html string) Result {
254 c.setStatusIfNil(http.StatusOK)
256 return &RenderHTMLResult{html}
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{
266 Description: "This action is not implemented",
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 {
275 finalText = fmt.Sprintf(msg, objs...)
277 c.Response.Status = http.StatusNotFound
278 return c.RenderError(&Error{
280 Description: finalText,
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 {
289 finalText = fmt.Sprintf(msg, objs...)
291 c.Response.Status = http.StatusForbidden
292 return c.RenderError(&Error{
294 Description: finalText,
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)
304 c.Log.Errorf("Cant open file: %v", err)
305 return c.RenderError(err)
307 return c.RenderFile(f, delivery)
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)
317 fileInfo, err = file.Stat()
320 controllerLog.Error("RenderFile: error", "error", err)
323 modtime = fileInfo.ModTime()
325 return c.RenderBinary(file, filepath.Base(file.Name()), delivery, modtime)
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)
336 return &BinaryResult{
340 Length: -1, // http.ServeContent gets the length itself unless memfile is a stream.
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)
352 if url, ok := val.(string); ok {
354 return &RedirectToURLResult{url}
356 return &RedirectToURLResult{fmt.Sprintf(url, args...)}
358 return &RedirectToActionResult{val, args}
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()
374 // Message performs a lookup for the given message name using the given
375 // arguments using the current language defined for this controller.
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...)
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 {
386 return c.SetTypeAction(controllerName, methodName, nil)
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 {
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)
398 c.Type = typeOfController
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)
406 c.Name, c.MethodName = c.Type.Type.Name(), c.MethodType.Name
407 c.Action = c.Name + "." + c.MethodName
409 // Update Logger with controller and namespace
411 c.Log = c.Log.New("action", c.Action, "namespace", c.Type.Namespace)
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,
422 return reflect.New(localType).Interface()
425 // Instantiate the controller.
426 c.AppController = RevelConfig.Controller.CachedMap[c.Name].Pop()
428 c.AppController = reflect.New(c.Type.Type).Interface()
430 c.setAppControllerFields()
435 func ControllerTypeByName(controllerName string, moduleSource *Module) (c *ControllerType) {
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)
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)
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))
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 {
481 appControllerPtr := reflect.New(appControllerType)
482 queue := []nodeType{{appControllerPtr, []int{}}}
484 // Get the next value and de-reference it if necessary.
488 elemType = elem.Type()
490 if elemType.Kind() == reflect.Ptr {
492 elemType = elem.Type()
496 // #944 if the type's Kind is not `Struct` move on,
497 // otherwise `elem.NumField()` will panic
498 if elemType.Kind() != reflect.Struct {
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 {
510 fieldValue := elem.Field(i)
511 fieldType := structField.Type
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))
519 queue = append(queue,
520 nodeType{fieldValue, append(append([]int{}, node.index...), i)})
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()
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()
540 // Fetch module for controller, if none found controller must be part of the app
541 controllerModule := ModuleFromPath(elem.PkgPath(), true)
543 controllerType := AddControllerType(controllerModule, elem, methods)
545 controllerLog.Debug("RegisterController:Registered controller", "controller", controllerType.Name())