Fixed bug with ComponentRegistration.Instance in which the instance type was not...
[castle.git] / MonoRail / Castle.MonoRail.Framework / Controller.cs
blobff7fb06b4ce35a325f016d7e16b7e9f0f4ae0ba8
1 // Copyright 2004-2008 Castle Project - http://www.castleproject.org/
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
15 namespace Castle.MonoRail.Framework
17 using System;
18 using System.Collections;
19 using System.Collections.Generic;
20 using System.Collections.Specialized;
21 using System.IO;
22 using System.Reflection;
23 using System.Web;
24 using Castle.Components.Binder;
25 using Castle.Components.Common.EmailSender;
26 using Castle.Components.Validator;
27 using Castle.Core.Logging;
28 using Castle.MonoRail.Framework.Descriptors;
29 using Castle.MonoRail.Framework.Resources;
30 using Castle.MonoRail.Framework.Internal;
31 using Core;
32 using Helpers;
34 /// <summary>
35 /// Implements the core functionality and exposes the
36 /// common methods for concrete controllers.
37 /// </summary>
38 public abstract class Controller : IController, IValidatorAccessor, IRedirectSupport
40 private IEngineContext engineContext;
41 private IControllerContext context;
42 private ILogger logger = NullLogger.Instance;
43 private bool directRenderInvoked;
44 private bool isContextualized;
46 private IHelperFactory helperFactory;
47 private IServiceInitializer serviceInitializer;
48 private IFilterFactory filterFactory;
49 private IViewEngineManager viewEngineManager;
50 private IActionSelector actionSelector;
51 private IScaffoldingSupport scaffoldSupport;
52 private FilterDescriptor[] filters = new FilterDescriptor[0];
53 private ValidatorRunner validatorRunner;
54 private Dictionary<object, ErrorSummary> validationSummaryPerInstance;
55 private Dictionary<object, ErrorList> boundInstances;
56 private ErrorSummary simplerErrorList = new ErrorSummary();
57 private RenderingSupport renderingSupport;
59 #region IController
61 /// <summary>
62 /// Occurs just before the action execution.
63 /// </summary>
64 public event ControllerHandler BeforeAction;
66 /// <summary>
67 /// Occurs just after the action execution.
68 /// </summary>
69 public event ControllerHandler AfterAction;
71 /// <summary>
72 /// Sets the context for the controller
73 /// </summary>
74 /// <param name="engineContext">The engine context.</param>
75 /// <param name="context">The controller context.</param>
76 public virtual void Contextualize(IEngineContext engineContext, IControllerContext context)
78 this.context = context;
79 SetEngineContext(engineContext);
80 renderingSupport = new RenderingSupport(context, engineContext);
81 isContextualized = true;
84 /// <summary>
85 /// Performs the specified action, which means:
86 /// <br/>
87 /// 1. Define the default view name<br/>
88 /// 2. Run the before filters<br/>
89 /// 3. Select the method related to the action name and invoke it<br/>
90 /// 4. On error, execute the rescues if available<br/>
91 /// 5. Run the after filters<br/>
92 /// 6. Invoke the view engine<br/>
93 /// </summary>
94 /// <param name="engineContext">The engine context.</param>
95 /// <param name="context">The controller context.</param>
96 public virtual void Process(IEngineContext engineContext, IControllerContext context)
98 if (isContextualized == false)
100 Contextualize(engineContext, context);
103 ResolveLayout();
104 CreateAndInitializeHelpers();
105 CreateFiltersDescriptors();
106 ProcessScaffoldIfAvailable();
107 ActionProviderUtil.RegisterActions(engineContext, this, context);
108 Initialize();
110 RunActionAndRenderView();
113 /// <summary>
114 /// Invoked by the view engine to perform
115 /// any logic before the view is sent to the client.
116 /// </summary>
117 /// <param name="view"></param>
118 public virtual void PreSendView(object view)
122 /// <summary>
123 /// Invoked by the view engine to perform
124 /// any logic after the view had been sent to the client.
125 /// </summary>
126 /// <param name="view"></param>
127 public virtual void PostSendView(object view)
131 /// <summary>
132 /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
133 /// </summary>
134 public virtual void Dispose()
136 DisposeFilters();
139 #endregion
141 #region IValidatorAccessor
143 /// <summary>
144 /// Gets the validator runner instance.
145 /// </summary>
146 /// <value>The validator instance.</value>
147 public ValidatorRunner Validator
151 if (validatorRunner == null)
153 validatorRunner = CreateValidatorRunner(engineContext.Services.ValidatorRegistry);
155 return validatorRunner;
157 set { validatorRunner = value; }
160 /// <summary>
161 /// Gets the bound instance errors. These are errors relative to
162 /// the binding process performed for the specified instance, nothing more.
163 /// </summary>
164 /// <value>The bound instance errors.</value>
165 public IDictionary<object, ErrorList> BoundInstanceErrors
169 if (boundInstances == null)
171 boundInstances = new Dictionary<object, ErrorList>();
173 return boundInstances;
177 /// <summary>
178 /// Populates the validator error summary with errors relative to the
179 /// validation rules associated with the target type.
180 /// </summary>
181 /// <param name="instance">The instance.</param>
182 /// <param name="binderUsedForBinding">The binder used for binding.</param>
183 public void PopulateValidatorErrorSummary(object instance, ErrorSummary binderUsedForBinding)
185 if (validationSummaryPerInstance == null)
187 validationSummaryPerInstance = new Dictionary<object, ErrorSummary>();
189 validationSummaryPerInstance[instance] = binderUsedForBinding;
192 /// <summary>
193 /// Gets the error summary associated with validation errors.
194 /// <para>
195 /// Will only work for instances populated by the <c>DataBinder</c>
196 /// </para>
197 /// </summary>
198 /// <param name="instance">object instance</param>
199 /// <returns>Error summary instance (can be null if the DataBinder wasn't configured to validate)</returns>
200 protected ErrorSummary GetErrorSummary(object instance)
202 if (validationSummaryPerInstance == null)
204 return null;
206 return validationSummaryPerInstance.ContainsKey(instance) ? validationSummaryPerInstance[instance] : null;
209 /// <summary>
210 /// Gets an error summary not associated with an object instance. This is useful
211 /// to register errors not associated with a data binding object.
212 /// </summary>
213 /// <value>The error summary.</value>
214 public ErrorSummary SimpleErrorSummary
216 get { return simplerErrorList; }
219 /// <summary>
220 /// Returns <c>true</c> if the given instance had
221 /// validation errors during binding.
222 /// <para>
223 /// Will only work for instances populated by the <c>DataBinder</c>
224 /// </para>
225 /// </summary>
226 /// <param name="instance">object instance</param>
227 /// <returns><c>true</c> if the validation had an error</returns>
228 protected bool HasValidationError(object instance)
230 ErrorSummary summary = GetErrorSummary(instance);
232 if (summary == null)
234 return false;
237 return summary.ErrorsCount != 0;
240 /// <summary>
241 /// Gets a list of errors that were thrown during the
242 /// object process, like conversion errors.
243 /// </summary>
244 /// <param name="instance">The instance that was populated by a binder.</param>
245 /// <returns>List of errors</returns>
246 protected ErrorList GetDataBindErrors(object instance)
248 if (boundInstances != null && boundInstances.ContainsKey(instance))
250 return boundInstances[instance];
252 return null;
255 #endregion
257 #region Useful Properties
259 /// <summary>
260 /// Gets the controller context.
261 /// </summary>
262 /// <value>The controller context.</value>
263 public IControllerContext ControllerContext
265 get { return context; }
266 set { context = value; }
269 /// <summary>
270 /// Gets the view folder -- (areaname +
271 /// controllername) or just controller name -- that this controller
272 /// will use by default.
273 /// </summary>
274 public string ViewFolder
276 get { return context.ViewFolder; }
277 set { context.ViewFolder = value; }
280 /// <summary>
281 /// This is intended to be used by MonoRail infrastructure.
282 /// </summary>
283 public ControllerMetaDescriptor MetaDescriptor
285 get { return context.ControllerDescriptor; }
286 set { context.ControllerDescriptor = value; }
289 /// <summary>
290 /// Gets the actions available in this controller.
291 /// </summary>
292 /// <remarks>It is supposed to be used by MonoRail infrastructure only</remarks>
293 /// <value>The actions.</value>
294 public ICollection Actions
296 get { return MetaDescriptor.Actions.Values; }
299 /// <summary>
300 /// Gets a dicitionary of name/<see cref="IResource"/>
301 /// </summary>
302 /// <remarks>It is supposed to be used by MonoRail infrastructure only</remarks>
303 /// <value>The resources.</value>
304 public IDictionary<string, IResource> Resources
306 get { return context.Resources; }
309 /// <summary>
310 /// Gets a dictionary of name/helper instance
311 /// </summary>
312 /// <value>The helpers.</value>
313 public IDictionary Helpers
315 get { return context.Helpers; }
318 /// <summary>
319 /// Gets a value indicating whether the request is a post.
320 /// </summary>
321 /// <value>
322 /// <see langword="true"/> if this request is a post; otherwise, <see langword="false"/>.
323 /// </value>
324 public bool IsPost
326 get { return engineContext.Request.HttpMethod == "POST"; }
329 /// <summary>
330 /// Gets a value indicating whether the request is a get.
331 /// </summary>
332 /// <value>
333 /// <see langword="true"/> if this request is a get; otherwise, <see langword="false"/>.
334 /// </value>
335 public bool IsGet
337 get { return engineContext.Request.HttpMethod == "GET"; }
340 /// <summary>
341 /// Gets a value indicating whether the request is a put.
342 /// </summary>
343 /// <value>
344 /// <see langword="true"/> if this request is a put; otherwise, <see langword="false"/>.
345 /// </value>
346 public bool IsPut
348 get { return engineContext.Request.HttpMethod == "PUT"; }
351 /// <summary>
352 /// Gets a value indicating whether the request is a head.
353 /// </summary>
354 /// <value>
355 /// <see langword="true"/> if this request is a head; otherwise, <see langword="false"/>.
356 /// </value>
357 public bool IsHead
359 get { return engineContext.Request.HttpMethod == "HEAD"; }
362 /// <summary>
363 /// Gets the controller's name.
364 /// </summary>
365 public string Name
367 get { return context.Name; }
370 /// <summary>
371 /// Gets the controller's area name.
372 /// </summary>
373 public string AreaName
375 get { return context.AreaName; }
378 /// <summary>
379 /// Gets or set the layout being used.
380 /// </summary>
381 public string LayoutName
383 get { return (context.LayoutNames != null && context.LayoutNames.Length != 0) ? context.LayoutNames[0] : null; }
386 if (value == null)
388 context.LayoutNames = null;
390 else
392 context.LayoutNames = new string[] { value };
397 /// <summary>
398 /// Gets or set the layouts being used.
399 /// </summary>
400 public string[] LayoutNames
402 get { return context.LayoutNames; }
403 set { context.LayoutNames = value; }
406 /// <summary>
407 /// Gets the name of the action being processed.
408 /// </summary>
409 public string Action
411 get { return context.Action; }
414 /// <summary>
415 /// Logger for the controller
416 /// </summary>
417 public ILogger Logger
419 get { return logger; }
420 set { logger = value; }
423 /// <summary>
424 /// Gets or sets the view which will be rendered by this action.
425 /// </summary>
426 public string SelectedViewName
428 get { return context.SelectedViewName; }
429 set { context.SelectedViewName = value; }
432 /// <summary>
433 /// Gets the property bag, which is used
434 /// to pass variables to the view.
435 /// </summary>
436 public IDictionary PropertyBag
438 get { return context.PropertyBag; }
439 set { context.PropertyBag = value; }
442 /// <summary>
443 /// Gets the context of this request execution.
444 /// </summary>
445 public IEngineContext Context
447 get { return engineContext; }
450 /// <summary>
451 /// Gets the Session dictionary.
452 /// </summary>
453 protected IDictionary Session
455 get { return engineContext.Session; }
458 /// <summary>
459 /// Gets a dictionary of volative items.
460 /// Ideal for showing success and failures messages.
461 /// </summary>
462 public Flash Flash
464 get { return engineContext.Flash; }
467 /// <summary>
468 /// Gets the web context of ASP.NET API.
469 /// </summary>
470 protected internal HttpContext HttpContext
472 get { return engineContext.UnderlyingContext; }
475 /// <summary>
476 /// Gets the request object.
477 /// </summary>
478 public IRequest Request
480 get { return Context.Request; }
483 /// <summary>
484 /// Gets the response object.
485 /// </summary>
486 public IResponse Response
488 get { return Context.Response; }
491 /// <summary>
492 /// Shortcut to <see cref="IRequest.Params"/>
493 /// </summary>
494 public NameValueCollection Params
496 get { return Request.Params; }
499 /// <summary>
500 /// Shortcut to <see cref="IRequest.Form"/>
501 /// </summary>
502 public NameValueCollection Form
504 get { return Request.Form; }
507 /// <summary>
508 /// Shortcut to <see cref="IRequest.QueryString"></see>
509 /// </summary>
510 public NameValueCollection Query
512 get { return Request.QueryString; }
515 /// <summary>
516 /// Gets the dynamic actions dictionary.
517 /// <para>
518 /// Can be used to insert dynamic actions on the controller instance.
519 /// </para>
520 /// </summary>
521 /// <value>The dynamic actions dictionary.</value>
522 public IDictionary<string, IDynamicAction> DynamicActions
524 get { return context.DynamicActions; }
527 /// <summary>
528 /// Shortcut to
529 /// <see cref="IResponse.IsClientConnected"/>
530 /// </summary>
531 protected bool IsClientConnected
533 get { return engineContext.Response.IsClientConnected; }
536 /// <summary>
537 /// Indicates that the current Action resulted from an ASP.NET PostBack.
538 /// As a result, this property is only relavent to controllers using
539 /// WebForms views. It is placed on the base Controller for convenience
540 /// only to avoid the need to extend the Controller or provide additional
541 /// helper classes. It is marked virtual to better support testing.
542 /// </summary>
543 protected virtual bool IsPostBack
547 NameValueCollection fields = Params;
548 return (fields["__VIEWSTATE"] != null) || (fields["__EVENTTARGET"] != null);
552 #endregion
554 #region Useful Operations
556 /// <summary>
557 /// Sets the engine context. Also initialize all required services by querying
558 /// <see cref="IEngineContext.Services"/>
559 /// </summary>
560 /// <param name="engineContext">The engine context.</param>
561 public virtual void SetEngineContext(IEngineContext engineContext)
563 this.engineContext = engineContext;
565 helperFactory = engineContext.Services.HelperFactory; // should not be null
566 serviceInitializer = engineContext.Services.ServiceInitializer; // should not be null
567 filterFactory = engineContext.Services.FilterFactory; // should not be null
568 viewEngineManager = engineContext.Services.ViewEngineManager; // should not be null
569 actionSelector = engineContext.Services.ActionSelector; // should not be null
570 scaffoldSupport = engineContext.Services.ScaffoldSupport; // might be null
573 #region From RenderingSupport
575 /// <summary>
576 /// Specifies the view to be processed after the action has finished its processing.
577 /// </summary>
578 /// <param name="name">view template name (the file extension is optional)</param>
579 public virtual void RenderView(string name)
581 renderingSupport.RenderView(name);
584 /// <summary>
585 /// Specifies the view to be processed after the action has finished its processing.
586 /// </summary>
587 /// <param name="name">view template name (the file extension is optional)</param>
588 /// <param name="skipLayout">If set to <c>true</c>, no layout will be used when rendering the view</param>
589 public virtual void RenderView(string name, bool skipLayout)
591 renderingSupport.RenderView(name, skipLayout);
594 /// <summary>
595 /// Specifies the view to be processed after the action has finished its processing.
596 /// </summary>
597 /// <param name="name">view template name (the file extension is optional)</param>
598 /// <param name="skipLayout">If set to <c>true</c>, no layout will be used when rendering the view</param>
599 /// <param name="mimeType">The mime type to use on the reply</param>
600 public virtual void RenderView(string name, bool skipLayout, string mimeType)
602 renderingSupport.RenderView(name, skipLayout, mimeType);
605 /// <summary>
606 /// Specifies the view to be processed after the action has finished its processing.
607 /// </summary>
608 /// <param name="controller">Controller name get view from (if you intend to user another controller's view</param>
609 /// <param name="name">view template name (the file extension is optional)</param>
610 public virtual void RenderView(string controller, string name)
612 renderingSupport.RenderView(controller, name);
615 /// <summary>
616 /// Specifies the view to be processed after the action has finished its processing.
617 /// </summary>
618 /// <param name="controller">Controller name get view from (if you intend to user another controller's view</param>
619 /// <param name="name">view template name (the file extension is optional)</param>
620 /// <param name="skipLayout">If set to <c>true</c>, no layout will be used when rendering the view</param>
621 public virtual void RenderView(string controller, string name, bool skipLayout)
623 renderingSupport.RenderView(controller, name, skipLayout);
626 /// <summary>
627 /// Specifies the view to be processed after the action has finished its processing.
628 /// </summary>
629 /// <param name="controller">Controller name get view from (if you intend to user another controller's view</param>
630 /// <param name="name">view template name (the file extension is optional)</param>
631 /// <param name="skipLayout">If set to <c>true</c>, no layout will be used when rendering the view</param>
632 /// <param name="mimeType">The mime type to use on the reply</param>
633 public virtual void RenderView(string controller, string name, bool skipLayout, string mimeType)
635 renderingSupport.RenderView(controller, name, skipLayout, mimeType);
638 /// <summary>
639 /// Specifies the view to be processed after the action has finished its processing.
640 /// </summary>
641 /// <param name="controller">Controller name get view from (if you intend to user another controller's view</param>
642 /// <param name="name">view template name (the file extension is optional)</param>
643 /// <param name="mimeType">The mime type to use on the reply</param>
644 public virtual void RenderView(string controller, string name, string mimeType)
646 renderingSupport.RenderView(controller, name, mimeType);
649 /// <summary>
650 /// Specifies the shared view to be processed after the action has finished its
651 /// processing. (A partial view shared
652 /// by others views and usually in the root folder
653 /// of the view directory).
654 /// </summary>
655 public virtual void RenderSharedView(string name)
657 renderingSupport.RenderSharedView(name);
660 /// <summary>
661 /// Specifies the shared view to be processed after the action has finished its
662 /// processing. (A partial view shared
663 /// by others views and usually in the root folder
664 /// of the view directory).
665 /// </summary>
666 public virtual void RenderSharedView(string name, bool skipLayout)
668 renderingSupport.RenderSharedView(name, skipLayout);
671 /// <summary>
672 /// Cancels the view processing.
673 /// </summary>
674 public virtual void CancelView()
676 renderingSupport.CancelView();
679 /// <summary>
680 /// Cancels the layout processing.
681 /// </summary>
682 public virtual void CancelLayout()
684 renderingSupport.CancelLayout();
687 /// <summary>
688 /// Cancels the view processing and writes
689 /// the specified contents to the browser
690 /// </summary>
691 public virtual void RenderText(string contents)
693 renderingSupport.RenderText(contents);
696 /// <summary>
697 /// Cancels the view processing and writes
698 /// the specified contents to the browser
699 /// </summary>
700 public virtual void RenderText(string contents, params object[] args)
702 renderingSupport.RenderText(contents, args);
705 /// <summary>
706 /// Cancels the view processing and writes
707 /// the specified contents to the browser
708 /// </summary>
709 public virtual void RenderText(IFormatProvider formatProvider, string contents, params object[] args)
711 renderingSupport.RenderText(formatProvider, contents, args);
714 #endregion
716 /// <summary>
717 /// Specifies the view to be processed and results are written to System.IO.TextWriter.
718 /// </summary>
719 /// <param name="output"></param>
720 /// <param name="name">The name of the view to process.</param>
721 public void InPlaceRenderView(TextWriter output, string name)
723 viewEngineManager.Process(Path.Combine(ViewFolder, name), output, Context, this, context);
726 /// <summary>
727 /// Specifies the shared view to be processed and results are written to System.IO.TextWriter.
728 /// (A partial view shared by others views and usually in the root folder
729 /// of the view directory).
730 /// </summary>
731 /// <param name="output"></param>
732 /// <param name="name">The name of the view to process.</param>
733 public void InPlaceRenderSharedView(TextWriter output, string name)
735 viewEngineManager.Process(name, output, Context, this, context);
738 /// <summary>
739 /// Sends raw contents to be rendered directly by the view engine.
740 /// It's up to the view engine just to apply the layout and nothing else.
741 /// </summary>
742 /// <param name="contents">Contents to be rendered.</param>
743 public void DirectRender(string contents)
745 CancelView();
747 if (directRenderInvoked)
749 throw new ControllerException("DirectRender should be called only once.");
752 directRenderInvoked = true;
754 viewEngineManager.RenderStaticWithinLayout(contents, engineContext, this, context);
757 /// <summary>
758 /// Returns true if the specified template exists.
759 /// </summary>
760 /// <param name="templateName"></param>
761 public bool HasTemplate(string templateName)
763 return viewEngineManager.HasTemplate(templateName);
766 #region Redirects
768 /// <summary>
769 /// Redirects to another action in the same controller.
770 /// </summary>
771 /// <param name="action">The action name</param>
772 public void RedirectToAction(string action)
774 RedirectToAction(action, (NameValueCollection)null);
777 /// <summary>
778 /// Redirects to another action in the same controller passing the specified querystring parameters.
779 /// </summary>
780 /// <param name="action">The action name</param>
781 /// <param name="queryStringParameters">The querystring entries</param>
782 public void RedirectToAction(string action, IDictionary queryStringParameters)
784 if (queryStringParameters != null)
786 Response.Redirect(AreaName, Name, TransformActionName(action), queryStringParameters);
788 else
790 Response.Redirect(AreaName, Name, TransformActionName(action));
794 /// <summary>
795 /// Redirects to another action in the same controller passing the specified querystring parameters.
796 /// </summary>
797 /// <param name="action">The action name</param>
798 /// <param name="queryStringParameters">The querystring entries</param>
799 public void RedirectToAction(string action, NameValueCollection queryStringParameters)
801 if (queryStringParameters != null)
803 Response.Redirect(AreaName, Name, TransformActionName(action), queryStringParameters);
805 else
807 Response.Redirect(AreaName, Name, TransformActionName(action));
811 /// <summary>
812 /// Redirects to another action in the same controller passing the specified querystring parameters.
813 /// </summary>
814 /// <param name="action">The action name</param>
815 /// <param name="queryStringParameters">The querystring entries</param>
816 public void RedirectToAction(string action, params string[] queryStringParameters)
818 RedirectToAction(action, DictHelper.Create(queryStringParameters));
821 /// <summary>
822 /// Redirects to another action in the same controller passing the specified querystring parameters.
823 /// </summary>
824 /// <param name="action">The action name</param>
825 /// <param name="queryStringAnonymousDictionary">The querystring entries as an anonymous dictionary</param>
826 public void RedirectToAction(string action, object queryStringAnonymousDictionary)
828 RedirectToAction(action, new ReflectionBasedDictionaryAdapter(queryStringAnonymousDictionary));
831 /// <summary>
832 /// Redirects to url using referrer.
833 /// </summary>
834 public void RedirectToReferrer()
836 Response.RedirectToReferrer();
839 /// <summary>
840 /// Redirects to the site root directory (<c>Context.ApplicationPath + "/"</c>).
841 /// </summary>
842 public void RedirectToSiteRoot()
844 Response.RedirectToSiteRoot();
847 /// <summary>
848 /// Redirects to the specified url
849 /// </summary>
850 /// <param name="url">An relative or absolute URL to redirect the client to</param>
851 public void RedirectToUrl(string url)
853 Response.RedirectToUrl(url);
856 /// <summary>
857 /// Redirects to the specified url
858 /// </summary>
859 /// <param name="url">An relative or absolute URL to redirect the client to</param>
860 /// <param name="endProcess">if set to <c>true</c>, sends the redirect and
861 /// kills the current request process.</param>
862 public void RedirectToUrl(string url, bool endProcess)
864 Response.RedirectToUrl(url, endProcess);
867 /// <summary>
868 /// Redirects to the specified url
869 /// </summary>
870 /// <param name="url">An relative or absolute URL to redirect the client to</param>
871 /// <param name="queryStringParameters">The querystring entries</param>
872 public void RedirectToUrl(string url, IDictionary queryStringParameters)
874 Response.RedirectToUrl(url, queryStringParameters);
877 /// <summary>
878 /// Redirects to the specified url
879 /// </summary>
880 /// <param name="url">An relative or absolute URL to redirect the client to</param>
881 /// <param name="queryStringParameters">The querystring entries</param>
882 public void RedirectToUrl(string url, NameValueCollection queryStringParameters)
884 Response.RedirectToUrl(url, queryStringParameters);
887 /// <summary>
888 /// Redirects to the specified url
889 /// </summary>
890 /// <param name="url">An relative or absolute URL to redirect the client to</param>
891 /// <param name="queryStringParameters">The querystring entries</param>
892 public void RedirectToUrl(string url, params string[] queryStringParameters)
894 Response.RedirectToUrl(url, queryStringParameters);
897 /// <summary>
898 /// Redirects to the specified url
899 /// </summary>
900 /// <param name="url">An relative or absolute URL to redirect the client to</param>
901 /// <param name="queryStringAnonymousDictionary">The querystring entries as an anonymous dictionary</param>
902 public void RedirectToUrl(string url, object queryStringAnonymousDictionary)
904 Response.RedirectToUrl(url, queryStringAnonymousDictionary);
907 /// <summary>
908 /// Redirects to another controller's action.
909 /// </summary>
910 /// <param name="controller">The controller name to be redirected to.</param>
911 /// <param name="action">The desired action on the target controller.</param>
912 public void Redirect(string controller, string action)
914 Response.Redirect(controller, action);
917 /// <summary>
918 /// Redirects to another controller's action with the specified parameters.
919 /// </summary>
920 /// <param name="controller">The controller name to be redirected to.</param>
921 /// <param name="action">The desired action on the target controller.</param>
922 /// <param name="queryStringParameters">The querystring entries</param>
923 public void Redirect(string controller, string action, NameValueCollection queryStringParameters)
925 Response.Redirect(controller, action, queryStringParameters);
928 /// <summary>
929 /// Redirects to another controller's action with the specified parameters.
930 /// </summary>
931 /// <param name="controller">The controller name to be redirected to.</param>
932 /// <param name="action">The desired action on the target controller.</param>
933 /// <param name="queryStringParameters">The querystring entries</param>
934 public void Redirect(string controller, string action, IDictionary queryStringParameters)
936 Response.Redirect(controller, action, queryStringParameters);
939 /// <summary>
940 /// Redirects to another controller's action with the specified parameters.
941 /// </summary>
942 /// <param name="controller">The controller name to be redirected to.</param>
943 /// <param name="action">The desired action on the target controller.</param>
944 /// <param name="queryStringAnonymousDictionary">The querystring entries as an anonymous dictionary</param>
945 public void Redirect(string controller, string action, object queryStringAnonymousDictionary)
947 Response.Redirect(controller, action, queryStringAnonymousDictionary);
950 /// <summary>
951 /// Redirects to another controller's action in a different area.
952 /// </summary>
953 /// <param name="area">The area the target controller belongs to.</param>
954 /// <param name="controller">The controller name to be redirected to.</param>
955 /// <param name="action">The desired action on the target controller.</param>
956 public void Redirect(string area, string controller, string action)
958 Response.Redirect(area, controller, action);
961 /// <summary>
962 /// Redirects to another controller's action in a different area with the specified parameters.
963 /// </summary>
964 /// <param name="area">The area the target controller belongs to.</param>
965 /// <param name="controller">The controller name to be redirected to.</param>
966 /// <param name="action">The desired action on the target controller.</param>
967 /// <param name="queryStringParameters">The querystring entries</param>
968 public void Redirect(string area, string controller, string action, IDictionary queryStringParameters)
970 Response.Redirect(area, controller, action, queryStringParameters);
973 /// <summary>
974 /// Redirects to another controller's action in a different area with the specified parameters.
975 /// </summary>
976 /// <param name="area">The area the target controller belongs to.</param>
977 /// <param name="controller">The controller name to be redirected to.</param>
978 /// <param name="action">The desired action on the target controller.</param>
979 /// <param name="queryStringParameters">The querystring entries</param>
980 public void Redirect(string area, string controller, string action, NameValueCollection queryStringParameters)
982 Response.Redirect(area, controller, action, queryStringParameters);
985 /// <summary>
986 /// Redirects to another controller's action in a different area with the specified parameters.
987 /// </summary>
988 /// <param name="area">The area the target controller belongs to.</param>
989 /// <param name="controller">The controller name to be redirected to.</param>
990 /// <param name="action">The desired action on the target controller.</param>
991 /// <param name="queryStringAnonymousDictionary">The querystring entries as an anonymous dictionary</param>
992 public void Redirect(string area, string controller, string action, object queryStringAnonymousDictionary)
994 Response.Redirect(area, controller, action, queryStringAnonymousDictionary);
997 /// <summary>
998 /// Redirects using a named route.
999 /// The name must exists otherwise a <see cref="MonoRailException"/> will be thrown.
1000 /// </summary>
1001 /// <param name="routeName">Route name.</param>
1002 public void RedirectUsingNamedRoute(string routeName)
1004 Response.RedirectUsingNamedRoute(routeName);
1007 /// <summary>
1008 /// Redirects using a named route.
1009 /// The name must exists otherwise a <see cref="MonoRailException"/> will be thrown.
1010 /// </summary>
1011 /// <param name="routeName">Route name.</param>
1012 /// <param name="routeParameters">The route parameters.</param>
1013 public void RedirectUsingNamedRoute(string routeName, object routeParameters)
1015 Response.RedirectUsingNamedRoute(routeName, routeParameters);
1018 /// <summary>
1019 /// Redirects using a named route.
1020 /// The name must exists otherwise a <see cref="MonoRailException"/> will be thrown.
1021 /// </summary>
1022 /// <param name="routeName">Route name.</param>
1023 /// <param name="routeParameters">The route parameters.</param>
1024 public void RedirectUsingNamedRoute(string routeName, IDictionary routeParameters)
1026 Response.RedirectUsingNamedRoute(routeName, routeParameters);
1029 /// <summary>
1030 /// Tries to resolve the target redirect url by using the routing rules registered.
1031 /// </summary>
1032 /// <param name="action">The desired action on the target controller.</param>
1033 /// <param name="useCurrentRouteParams">if set to <c>true</c> the current request matching route rules will be used.</param>
1034 public void RedirectUsingRoute(string action, bool useCurrentRouteParams)
1036 Response.RedirectUsingRoute(Name, action, useCurrentRouteParams);
1039 /// <summary>
1040 /// Tries to resolve the target redirect url by using the routing rules registered.
1041 /// </summary>
1042 /// <param name="action">The desired action on the target controller.</param>
1043 /// <param name="routeParameters">The routing rule parameters.</param>
1044 public void RedirectUsingRoute(string action, IDictionary routeParameters)
1046 Response.RedirectUsingRoute(Name, action, routeParameters);
1049 /// <summary>
1050 /// Tries to resolve the target redirect url by using the routing rules registered.
1051 /// </summary>
1052 /// <param name="action">The desired action on the target controller.</param>
1053 /// <param name="routeParameters">The routing rule parameters.</param>
1054 public void RedirectUsingRoute(string action, object routeParameters)
1056 Response.RedirectUsingRoute(Name, action, routeParameters);
1059 /// <summary>
1060 /// Tries to resolve the target redirect url by using the routing rules registered.
1061 /// </summary>
1062 /// <param name="controller">The controller name to be redirected to.</param>
1063 /// <param name="action">The desired action on the target controller.</param>
1064 /// <param name="useCurrentRouteParams">if set to <c>true</c> the current request matching route rules will be used.</param>
1065 public void RedirectUsingRoute(string controller, string action, bool useCurrentRouteParams)
1067 Response.RedirectUsingRoute(controller, action, useCurrentRouteParams);
1070 /// <summary>
1071 /// Tries to resolve the target redirect url by using the routing rules registered.
1072 /// </summary>
1073 /// <param name="area">The area the target controller belongs to.</param>
1074 /// <param name="controller">The controller name to be redirected to.</param>
1075 /// <param name="action">The desired action on the target controller.</param>
1076 /// <param name="useCurrentRouteParams">if set to <c>true</c> the current request matching route rules will be used.</param>
1077 public void RedirectUsingRoute(string area, string controller, string action, bool useCurrentRouteParams)
1079 Response.RedirectUsingRoute(area, controller, action, useCurrentRouteParams);
1082 /// <summary>
1083 /// Tries to resolve the target redirect url by using the routing rules registered.
1084 /// </summary>
1085 /// <param name="controller">The controller name to be redirected to.</param>
1086 /// <param name="action">The desired action on the target controller.</param>
1087 /// <param name="routeParameters">The routing rule parameters.</param>
1088 public void RedirectUsingRoute(string controller, string action, IDictionary routeParameters)
1090 Response.RedirectUsingRoute(controller, action, routeParameters);
1093 /// <summary>
1094 /// Tries to resolve the target redirect url by using the routing rules registered.
1095 /// </summary>
1096 /// <param name="controller">The controller name to be redirected to.</param>
1097 /// <param name="action">The desired action on the target controller.</param>
1098 /// <param name="routeParameters">The routing rule parameters.</param>
1099 public void RedirectUsingRoute(string controller, string action, object routeParameters)
1101 Response.RedirectUsingRoute(controller, action, routeParameters);
1104 /// <summary>
1105 /// Tries to resolve the target redirect url by using the routing rules registered.
1106 /// </summary>
1107 /// <param name="area">The area the target controller belongs to.</param>
1108 /// <param name="controller">The controller name to be redirected to.</param>
1109 /// <param name="action">The desired action on the target controller.</param>
1110 /// <param name="routeParameters">The routing rule parameters.</param>
1111 public void RedirectUsingRoute(string area, string controller, string action, IDictionary routeParameters)
1113 Response.RedirectUsingRoute(area, controller, action, routeParameters);
1116 /// <summary>
1117 /// Tries to resolve the target redirect url by using the routing rules registered.
1118 /// </summary>
1119 /// <param name="area">The area the target controller belongs to.</param>
1120 /// <param name="controller">The controller name to be redirected to.</param>
1121 /// <param name="action">The desired action on the target controller.</param>
1122 /// <param name="routeParameters">The routing rule parameters.</param>
1123 public void RedirectUsingRoute(string area, string controller, string action, object routeParameters)
1125 Response.RedirectUsingRoute(area, controller, action, routeParameters);
1128 #endregion
1130 #region Redirect utilities
1132 /// <summary>
1133 /// Creates a querystring string representation of the namevalue collection.
1134 /// </summary>
1135 /// <param name="parameters">The parameters.</param>
1136 /// <returns></returns>
1137 protected string ToQueryString(NameValueCollection parameters)
1139 return CommonUtils.BuildQueryString(Context.Server, parameters, false);
1142 /// <summary>
1143 /// Creates a querystring string representation of the entries in the dictionary.
1144 /// </summary>
1145 /// <param name="parameters">The parameters.</param>
1146 /// <returns></returns>
1147 protected string ToQueryString(IDictionary parameters)
1149 return CommonUtils.BuildQueryString(Context.Server, parameters, false);
1152 #endregion
1154 #endregion
1156 #region Email operations
1158 /// <summary>
1159 /// Creates an instance of <see cref="Message"/>
1160 /// using the specified template for the body
1161 /// </summary>
1162 /// <param name="templateName">
1163 /// Name of the template to load.
1164 /// Will look in Views/mail for that template file.
1165 /// </param>
1166 /// <returns>An instance of <see cref="Message"/></returns>
1167 [Obsolete]
1168 public Message RenderMailMessage(string templateName)
1170 return RenderMailMessage(templateName, false);
1173 /// <summary>
1174 /// Creates an instance of <see cref="Message"/>
1175 /// using the specified template for the body
1176 /// </summary>
1177 /// <param name="templateName">
1178 /// Name of the template to load.
1179 /// Will look in Views/mail for that template file.
1180 /// </param>
1181 /// <param name="doNotApplyLayout">If <c>true</c>, it will skip the layout</param>
1182 /// <returns>An instance of <see cref="Message"/></returns>
1183 [Obsolete]
1184 public Message RenderMailMessage(string templateName, bool doNotApplyLayout)
1186 IEmailTemplateService templateService = engineContext.Services.EmailTemplateService;
1187 return templateService.RenderMailMessage(templateName, Context, this, ControllerContext, doNotApplyLayout);
1190 /// <summary>
1191 /// Creates an instance of <see cref="Message"/>
1192 /// using the specified template for the body
1193 /// </summary>
1194 /// <param name="templateName">Name of the template to load.
1195 /// Will look in Views/mail for that template file.</param>
1196 /// <param name="layoutName">Name of the layout.</param>
1197 /// <param name="parameters">The parameters.</param>
1198 /// <returns>An instance of <see cref="Message"/></returns>
1199 public Message RenderMailMessage(string templateName, string layoutName, IDictionary parameters)
1201 IEmailTemplateService templateService = engineContext.Services.EmailTemplateService;
1202 return templateService.RenderMailMessage(templateName, layoutName, parameters);
1205 /// <summary>
1206 /// Creates an instance of <see cref="Message"/>
1207 /// using the specified template for the body
1208 /// </summary>
1209 /// <param name="templateName">Name of the template to load.
1210 /// Will look in Views/mail for that template file.</param>
1211 /// <param name="layoutName">Name of the layout.</param>
1212 /// <param name="parameters">The parameters.</param>
1213 /// <returns>An instance of <see cref="Message"/></returns>
1214 public Message RenderMailMessage(string templateName, string layoutName, IDictionary<string, object> parameters)
1216 IEmailTemplateService templateService = engineContext.Services.EmailTemplateService;
1217 return templateService.RenderMailMessage(templateName, layoutName, parameters);
1220 /// <summary>
1221 /// Creates an instance of <see cref="Message"/>
1222 /// using the specified template for the body
1223 /// </summary>
1224 /// <param name="templateName">Name of the template to load.
1225 /// Will look in Views/mail for that template file.</param>
1226 /// <param name="layoutName">Name of the layout.</param>
1227 /// <param name="parameters">The parameters.</param>
1228 /// <returns>An instance of <see cref="Message"/></returns>
1229 public Message RenderMailMessage(string templateName, string layoutName, object parameters)
1231 IEmailTemplateService templateService = engineContext.Services.EmailTemplateService;
1232 return templateService.RenderMailMessage(templateName, layoutName, parameters);
1235 /// <summary>
1236 /// Attempts to deliver the Message using the server specified on the web.config.
1237 /// </summary>
1238 /// <param name="message">The instance of System.Web.Mail.MailMessage that will be sent</param>
1239 public void DeliverEmail(Message message)
1243 IEmailSender sender = engineContext.Services.EmailSender;
1244 sender.Send(message);
1246 catch (Exception ex)
1248 if (logger.IsErrorEnabled)
1250 logger.Error("Error sending e-mail", ex);
1253 throw new MonoRailException("Error sending e-mail", ex);
1257 /// <summary>
1258 /// Renders and delivers the e-mail message.
1259 /// <seealso cref="DeliverEmail"/>
1260 /// </summary>
1261 /// <param name="templateName"></param>
1262 [Obsolete]
1263 public void RenderEmailAndSend(string templateName)
1265 Message message = RenderMailMessage(templateName);
1266 DeliverEmail(message);
1269 #endregion
1271 #region Lifecycle (overridables)
1273 /// <summary>
1274 /// Initializes this instance. Implementors
1275 /// can use this method to perform initialization
1276 /// </summary>
1277 public virtual void Initialize()
1281 #endregion
1283 #region Resources/i18n
1285 /// <summary>
1286 /// Creates the controller level resources.
1287 /// </summary>
1288 protected virtual void CreateControllerLevelResources()
1290 CreateResources(MetaDescriptor.Resources);
1293 /// <summary>
1294 /// Creates the controller level resources.
1295 /// </summary>
1296 /// <param name="action">The action.</param>
1297 protected virtual void CreateActionLevelResources(IExecutableAction action)
1299 CreateResources(action.Resources);
1302 /// <summary>
1303 /// Creates the resources and adds them to the <see cref="IControllerContext.Resources"/>.
1304 /// </summary>
1305 /// <param name="resources">The resources.</param>
1306 protected virtual void CreateResources(ResourceDescriptor[] resources)
1308 if (resources == null || resources.Length == 0)
1310 return;
1313 Assembly typeAssembly = GetType().Assembly;
1315 IResourceFactory resourceFactory = engineContext.Services.ResourceFactory;
1317 foreach (ResourceDescriptor resDesc in resources)
1319 if (ControllerContext.Resources.ContainsKey(resDesc.Name))
1321 throw new MonoRailException("There is a duplicated entry on the resource dictionary. Resource entry name: " +
1322 resDesc.Name);
1325 ControllerContext.Resources.Add(resDesc.Name, resourceFactory.Create(resDesc, typeAssembly));
1329 #endregion
1331 /// <summary>
1332 /// Gives a change to subclass
1333 /// to override the layout resolution code
1334 /// </summary>
1335 protected virtual void ResolveLayout()
1337 context.LayoutNames = ObtainDefaultLayoutName();
1340 private void RunActionAndRenderView()
1342 IExecutableAction action = null;
1343 Exception actionException = null;
1344 bool cancel;
1348 action = SelectAction(Action);
1350 if (action == null)
1352 throw new MonoRailException(404, "Not Found", "Could not find action named " +
1353 Action + " on controller " + AreaName + "\\" + Name);
1356 EnsureActionIsAccessibleWithCurrentHttpVerb(action);
1358 RunBeforeActionFilters(action, out cancel);
1360 CreateControllerLevelResources();
1361 CreateActionLevelResources(action);
1363 if (cancel) return;
1365 if (BeforeAction != null)
1367 BeforeAction(action, engineContext, this, context);
1370 object actionRetValue = action.Execute(engineContext, this, context);
1372 // TO DO: review/refactor this code
1373 if (action.ReturnBinderDescriptor != null)
1375 IReturnBinder binder = action.ReturnBinderDescriptor.ReturnTypeBinder;
1377 // Runs return binder and keep going
1378 binder.Bind(Context, this, ControllerContext, action.ReturnBinderDescriptor.ReturnType, actionRetValue);
1381 // Action executed successfully, so it's safe to process the cache configurer
1382 if ((MetaDescriptor.CacheConfigurer != null || action.CachePolicyConfigurer != null) &&
1383 !Response.WasRedirected && Response.StatusCode == 200)
1385 ConfigureCachePolicy(action);
1388 catch (MonoRailException ex)
1390 if (Response.StatusCode == 200 && ex.HttpStatusCode.HasValue)
1392 Response.StatusCode = ex.HttpStatusCode.Value;
1393 Response.StatusDescription = ex.HttpStatusDesc;
1396 actionException = ex;
1398 RegisterExceptionAndNotifyExtensions(actionException);
1400 RunAfterActionFilters(action, out cancel);
1402 if (!ProcessRescue(action, actionException))
1404 throw;
1406 return;
1408 catch (Exception ex)
1410 if (Response.StatusCode == 200)
1412 Response.StatusCode = 500;
1413 Response.StatusDescription = "Error processing action";
1416 actionException = (ex is TargetInvocationException) ? ex.InnerException : ex;
1418 RegisterExceptionAndNotifyExtensions(actionException);
1420 RunAfterActionFilters(action, out cancel);
1422 if (!ProcessRescue(action, actionException))
1424 throw;
1426 return;
1428 finally
1430 // AfterAction event: always executed
1431 if (AfterAction != null)
1433 AfterAction(action, engineContext, this, context);
1437 RunAfterActionFilters(action, out cancel);
1438 if (cancel) return;
1440 if (engineContext.Response.WasRedirected) // No need to process view or rescue in this case
1442 return;
1445 if (context.SelectedViewName != null)
1447 ProcessView();
1448 RunAfterRenderingFilters(action);
1452 /// <summary>
1453 /// Configures the cache policy.
1454 /// </summary>
1455 /// <param name="action">The action.</param>
1456 protected virtual void ConfigureCachePolicy(IExecutableAction action)
1458 ICachePolicyConfigurer configurer = action.CachePolicyConfigurer ?? MetaDescriptor.CacheConfigurer;
1460 configurer.Configure(Response.CachePolicy);
1463 /// <summary>
1464 /// Selects the appropriate action.
1465 /// </summary>
1466 /// <param name="action">The action name.</param>
1467 /// <returns></returns>
1468 protected virtual IExecutableAction SelectAction(string action)
1470 // For backward compatibility purposes
1471 MethodInfo method = SelectMethod(action, MetaDescriptor.Actions, engineContext.Request, context.CustomActionParameters);
1473 if (method != null)
1475 ActionMetaDescriptor actionMeta = MetaDescriptor.GetAction(method);
1477 return new ActionMethodExecutorCompatible(method, actionMeta ?? new ActionMetaDescriptor(), InvokeMethod);
1480 // New supported way
1481 return actionSelector.Select(engineContext, this, context);
1484 /// <summary>
1485 /// Invokes the scaffold support if the controller
1486 /// is associated with a scaffold
1487 /// </summary>
1488 protected virtual void ProcessScaffoldIfAvailable()
1490 if (MetaDescriptor.Scaffoldings.Count != 0)
1492 if (scaffoldSupport == null)
1494 String message = "You must enable scaffolding support on the " +
1495 "configuration file, or, to use the standard ActiveRecord support " +
1496 "copy the necessary assemblies to the bin folder.";
1498 throw new MonoRailException(message);
1501 scaffoldSupport.Process(engineContext, this, context);
1505 /// <summary>
1506 /// Ensures the action is accessible with current HTTP verb.
1507 /// </summary>
1508 /// <param name="action">The action.</param>
1509 protected virtual void EnsureActionIsAccessibleWithCurrentHttpVerb(IExecutableAction action)
1511 Verb allowedVerbs = action.AccessibleThroughVerb;
1513 if (allowedVerbs == Verb.Undefined)
1515 return;
1518 string method = engineContext.Request.HttpMethod;
1520 Verb currentVerb = (Verb)Enum.Parse(typeof(Verb), method, true);
1522 if ((allowedVerbs & currentVerb) != currentVerb)
1524 throw new MonoRailException(403, "Forbidden",
1525 string.Format("Access to the action [{0}] " +
1526 "on controller [{1}] is not allowed to the http verb [{2}].",
1527 Action, Name, method));
1531 #region Views and Layout
1533 /// <summary>
1534 /// Obtains the name of the default layout.
1535 /// </summary>
1536 /// <returns></returns>
1537 protected virtual String[] ObtainDefaultLayoutName()
1539 if (MetaDescriptor.Layout != null)
1541 return MetaDescriptor.Layout.LayoutNames;
1543 else
1545 String defaultLayout = String.Format("layouts/{0}", Name);
1547 if (viewEngineManager.HasTemplate(defaultLayout))
1549 return new String[] { Name };
1553 return null;
1556 /// <summary>
1557 /// Processes the view.
1558 /// </summary>
1559 protected virtual void ProcessView()
1561 if (context.SelectedViewName != null)
1563 viewEngineManager.Process(context.SelectedViewName, engineContext.Response.Output, engineContext, this, context);
1567 #endregion
1569 #region Helpers
1571 /// <summary>
1572 /// Creates the and initialize helpers associated with a controller.
1573 /// </summary>
1574 public virtual void CreateAndInitializeHelpers()
1576 IDictionary helpers = context.Helpers;
1578 // Custom helpers
1580 foreach (HelperDescriptor helper in MetaDescriptor.Helpers)
1582 bool initialized;
1583 object helperInstance = helperFactory.Create(helper.HelperType, engineContext, out initialized);
1585 if (!initialized)
1587 serviceInitializer.Initialize(helperInstance, engineContext);
1590 if (helpers.Contains(helper.Name))
1592 throw new ControllerException(String.Format("Found a duplicate helper " +
1593 "attribute named '{0}' on controller '{1}'", helper.Name, Name));
1596 helpers.Add(helper.Name, helperInstance);
1599 CreateStandardHelpers();
1602 /// <summary>
1603 /// Creates the standard helpers.
1604 /// </summary>
1605 public virtual void CreateStandardHelpers()
1607 AbstractHelper[] builtInHelpers =
1608 new AbstractHelper[]
1610 new AjaxHelper(engineContext), new BehaviourHelper(engineContext),
1611 new UrlHelper(engineContext), new TextHelper(engineContext),
1612 new EffectsFatHelper(engineContext), new ScriptaculousHelper(engineContext),
1613 new DateFormatHelper(engineContext), new HtmlHelper(engineContext),
1614 new ValidationHelper(engineContext), new DictHelper(engineContext),
1615 new PaginationHelper(engineContext), new FormHelper(engineContext),
1616 new JSONHelper(engineContext), new ZebdaHelper(engineContext)
1619 foreach (AbstractHelper helper in builtInHelpers)
1621 context.Helpers.Add(helper);
1623 if (helper is IServiceEnabledComponent)
1625 serviceInitializer.Initialize(helper, engineContext);
1630 #endregion
1632 #region Filters
1634 private void CreateFiltersDescriptors()
1636 if (MetaDescriptor.Filters.Length != 0)
1638 filters = CopyFilterDescriptors();
1642 private void RunBeforeActionFilters(IExecutableAction action, out bool cancel)
1644 cancel = false;
1645 if (action.ShouldSkipAllFilters) return;
1647 if (!ProcessFilters(action, ExecuteWhen.BeforeAction))
1649 cancel = true;
1650 return; // A filter returned false... so we stop
1654 private void RunAfterActionFilters(IExecutableAction action, out bool cancel)
1656 cancel = false;
1657 if (action == null) return;
1659 if (action.ShouldSkipAllFilters) return;
1661 if (!ProcessFilters(action, ExecuteWhen.AfterAction))
1663 cancel = true;
1664 return; // A filter returned false... so we stop
1668 private void RunAfterRenderingFilters(IExecutableAction action)
1670 if (action.ShouldSkipAllFilters) return;
1672 ProcessFilters(action, ExecuteWhen.AfterRendering);
1675 /// <summary>
1676 /// Identifies if no filter should run for the given action.
1677 /// </summary>
1678 /// <param name="action">The action.</param>
1679 /// <returns></returns>
1680 protected virtual bool ShouldSkipFilters(IExecutableAction action)
1682 if (filters == null)
1684 // No filters, so skip
1685 return true;
1688 return action.ShouldSkipAllFilters;
1690 // ActionMetaDescriptor actionMeta = MetaDescriptor.GetAction(method);
1692 // if (actionMeta.SkipFilters.Count == 0)
1693 // {
1694 // // Nothing against filters declared for this action
1695 // return false;
1696 // }
1698 // foreach(SkipFilterAttribute skipfilter in actionMeta.SkipFilters)
1699 // {
1700 // // SkipAllFilters handling...
1701 // if (skipfilter.BlanketSkip)
1702 // {
1703 // return true;
1704 // }
1706 // filtersToSkip[skipfilter.FilterType] = String.Empty;
1707 // }
1709 // return false;
1712 /// <summary>
1713 /// Clones all Filter descriptors, in order to get a writable copy.
1714 /// </summary>
1715 protected internal FilterDescriptor[] CopyFilterDescriptors()
1717 FilterDescriptor[] clone = (FilterDescriptor[])MetaDescriptor.Filters.Clone();
1719 for (int i = 0; i < clone.Length; i++)
1721 clone[i] = (FilterDescriptor)clone[i].Clone();
1724 return clone;
1727 private bool ProcessFilters(IExecutableAction action, ExecuteWhen when)
1729 foreach (FilterDescriptor desc in filters)
1731 if (action.ShouldSkipFilter(desc.FilterType))
1733 continue;
1736 if ((desc.When & when) != 0)
1738 if (!ProcessFilter(when, desc))
1740 return false;
1745 return true;
1748 private bool ProcessFilter(ExecuteWhen when, FilterDescriptor desc)
1750 if (desc.FilterInstance == null)
1752 desc.FilterInstance = filterFactory.Create(desc.FilterType);
1754 IFilterAttributeAware filterAttAware = desc.FilterInstance as IFilterAttributeAware;
1756 if (filterAttAware != null)
1758 filterAttAware.Filter = desc.Attribute;
1764 if (logger.IsDebugEnabled)
1766 logger.DebugFormat("Running filter {0}/{1}", when, desc.FilterType.FullName);
1769 return desc.FilterInstance.Perform(when, engineContext, this, context);
1771 catch (Exception ex)
1773 if (logger.IsErrorEnabled)
1775 logger.ErrorFormat("Error processing filter " + desc.FilterType.FullName, ex);
1778 throw;
1782 private void DisposeFilters()
1784 if (filters == null) return;
1786 foreach (FilterDescriptor desc in filters)
1788 if (desc.FilterInstance != null)
1790 filterFactory.Release(desc.FilterInstance);
1795 #endregion
1797 #region Rescue
1799 /// <summary>
1800 /// Performs the rescue.
1801 /// </summary>
1802 /// <param name="action">The action (can be null in the case of dynamic actions).</param>
1803 /// <param name="actionException">The exception.</param>
1804 /// <returns></returns>
1805 protected virtual bool ProcessRescue(IExecutableAction action, Exception actionException)
1807 if (action != null && action.ShouldSkipRescues)
1809 return false;
1812 Type exceptionType = actionException.GetType();
1814 RescueDescriptor desc = action != null ? action.GetRescueFor(exceptionType) : null;
1816 if (desc == null)
1818 desc = GetControllerRescueFor(exceptionType);
1821 if (desc != null)
1825 if (desc.RescueController != null)
1827 CreateAndProcessRescueController(desc, actionException);
1829 else
1831 context.SelectedViewName = Path.Combine("rescues", desc.ViewName);
1833 ProcessView();
1836 return true;
1838 catch(Exception exception)
1840 // In this situation, the rescue view could not be found
1841 // So we're back to the default error exibition
1843 if (logger.IsFatalEnabled)
1845 logger.FatalFormat("Failed to process rescue view. View name " +
1846 context.SelectedViewName, exception);
1851 return false;
1854 /// <summary>
1855 /// Gets the best rescue that matches the exception type
1856 /// </summary>
1857 /// <param name="exceptionType">Type of the exception.</param>
1858 /// <returns></returns>
1859 protected virtual RescueDescriptor GetControllerRescueFor(Type exceptionType)
1861 return RescueUtils.SelectBest(MetaDescriptor.Rescues, exceptionType);
1864 private void CreateAndProcessRescueController(RescueDescriptor desc, Exception actionException)
1866 IController rescueController = engineContext.Services.ControllerFactory.CreateController(desc.RescueController);
1868 ControllerMetaDescriptor rescueControllerMeta =
1869 engineContext.Services.ControllerDescriptorProvider.BuildDescriptor(rescueController);
1871 ControllerDescriptor rescueControllerDesc = rescueControllerMeta.ControllerDescriptor;
1873 IControllerContext rescueControllerContext = engineContext.Services.ControllerContextFactory.Create(
1874 rescueControllerDesc.Area, rescueControllerDesc.Name, desc.RescueMethod.Name,
1875 rescueControllerMeta);
1877 rescueControllerContext.CustomActionParameters["exception"] = actionException;
1878 rescueControllerContext.CustomActionParameters["controller"] = this;
1879 rescueControllerContext.CustomActionParameters["controllerContext"] = ControllerContext;
1881 rescueController.Process(engineContext, rescueControllerContext);
1884 #endregion
1886 /// <summary>
1887 /// Pendent
1888 /// </summary>
1889 /// <param name="action">The action.</param>
1890 /// <param name="actions">The actions.</param>
1891 /// <param name="request">The request.</param>
1892 /// <param name="actionArgs">The action args.</param>
1893 /// <returns></returns>
1894 protected virtual MethodInfo SelectMethod(string action, IDictionary actions, IRequest request,
1895 IDictionary<string, object> actionArgs)
1897 return null;
1900 /// <summary>
1901 /// Pendent
1902 /// </summary>
1903 /// <param name="method">The method.</param>
1904 /// <param name="request">The request.</param>
1905 /// <param name="methodArgs">The method args.</param>
1906 protected virtual object InvokeMethod(MethodInfo method, IRequest request,
1907 IDictionary<string, object> methodArgs)
1909 return method.Invoke(this, new object[0]);
1912 /// <summary>
1913 /// Creates the default validator runner.
1914 /// </summary>
1915 /// <param name="validatorRegistry">The validator registry.</param>
1916 /// <returns></returns>
1917 /// <remarks>
1918 /// You can override this method to create a runner
1919 /// with some different configuration
1920 /// </remarks>
1921 protected virtual ValidatorRunner CreateValidatorRunner(IValidatorRegistry validatorRegistry)
1923 if (validatorRegistry == null)
1925 throw new ArgumentNullException("validatorRegistry");
1928 return new ValidatorRunner(validatorRegistry);
1931 /// <summary>
1932 /// Gives a chance to subclasses to format the action name properly
1933 /// </summary>
1934 /// <param name="action">Raw action name</param>
1935 /// <returns>Properly formatted action name</returns>
1936 protected virtual string TransformActionName(string action)
1938 return action;
1941 /// <summary>
1942 /// Registers the exception and notify extensions.
1943 /// </summary>
1944 /// <param name="exception">The exception.</param>
1945 protected void RegisterExceptionAndNotifyExtensions(Exception exception)
1947 engineContext.LastException = exception;
1948 engineContext.Services.ExtensionManager.RaiseActionError(engineContext);