How to make a controller that is RenderMvc and Surface?
I have a contact page called "ContactPage".
Now I have a custom view model. I understand that I need a ContactPageController : SurfaceController to handle post data. But how to I render the initial page with with my view model. Because if it was a RenderMvc controller I could just return CurrentTemplate(new ContactPageModel(...));. But I can't do that with the surface controller.
So what is the solution to this? Can't I have the POST version of my contact page with the same name as the GET page?
The SurfaceController has been designed in a specific way to be auto-routed and to handle a POST request from the front end of Umbraco, it kind of sits on the Surface...
Some people would say that a POST request in MVC shouldn't return a View, but instead should only ever 'process the POST' and then redirect to a success GET action...
... and SurfaceControllers are kinda setup to do this kind of thing eg
You handle the POST and then return 'CurrentUmbracoPage()' or 'RedirectToCurrentUmbracoPage()' to place control back into the underlying RenderMvcController, and you can pass values by the querystring from the SurfaceController (or viewbag) if needs be.
Or you can RedirectToUmbracoPage() or RedirectToUmbracoUrl() to send the request, once the POST is handled to a GET endpoint that will return a view.
So with this proposed pattern of working, say if you had an Advanced Search Form, you might have a SurfaceController handle the POST of the form, validate the values supplied are valid (return CurrentUmbracoPage if not to show validation errors) - then if all valid, Redirect to another Umbraco page, that has a hijacked RenderMvcController to take in the values of the form, passed via Querystring, that then 'does the search' and returns the View/Template that displays the search results...
... but alot of people prefer to have that all in one Controller ...
which is possible (people call this hybrid controllers) if you create a new controller, and make it inherit from SurfaceController AND implement IRenderMvcController
eg people tend to end up with something like this as a base controller to inherit from:
namespace MyLovely.Controllers.Base
{
public abstract class HybridSurfaceRenderMvcController : SurfaceController, IRenderMvcController
{
/// <summary>
/// Checks to make sure the physical view file exists on disk.
/// </summary>
/// <param name="template"></param>
/// <returns></returns>
protected bool EnsurePhysicalViewExists(string template)
{
try
{
var result = ViewEngines.Engines.FindView(ControllerContext, template, null);
if (result.View == null)
{
LogHelper.Warn<HybridSurfaceRenderMvcController>("No physical template file was found for template " + template);
return false;
}
}
catch (Exception ex)
{
// Log the exception.
LogHelper.Error<HybridSurfaceRenderMvcController>("Surface Render Mvc Controller Error: EnsurePhysicalViewExists", ex);
}
return true;
}
/// <summary>
/// Returns an ActionResult based on the template name found in the route values and the given model.
/// If the template found in the route values doesn't physically exist, then an empty ContentResult will be returned.
/// </remarks>
protected ActionResult CurrentTemplate<T>(T model)
{
var template = ControllerContext.RouteData.Values["action"].ToString();
if (!this.EnsurePhysicalViewExists(template))
{
return this.HttpNotFound();
}
return this.View(template, model);
}
/// <summary>
/// The default action to render the front-end view.
/// </summary>
public virtual ActionResult Index(ContentModel model)
{
return this.CurrentTemplate(model);
}
}
Then this would be able to 'hijack' a RenderMvcController route by convention, and handle a SurfaceController postback 'in the same file'.
Personally I tend to keep them separate, over the years, I just seem to prefer it, when debugging something.
But hopefully that gives you a bit of an insight into what's going on and allows you to form your own approach!
How to make a controller that is RenderMvc and Surface?
I have a contact page called "ContactPage".
Now I have a custom view model. I understand that I need a
ContactPageController : SurfaceController
to handle post data. But how to I render the initial page with with my view model. Because if it was aRenderMvc
controller I could justreturn CurrentTemplate(new ContactPageModel(...));
. But I can't do that with the surface controller.So what is the solution to this? Can't I have the POST version of my contact page with the same name as the GET page?
Hi Roy
The SurfaceController has been designed in a specific way to be auto-routed and to handle a POST request from the front end of Umbraco, it kind of sits on the Surface...
Some people would say that a POST request in MVC shouldn't return a View, but instead should only ever 'process the POST' and then redirect to a success GET action...
... and SurfaceControllers are kinda setup to do this kind of thing eg
You handle the POST and then return 'CurrentUmbracoPage()' or 'RedirectToCurrentUmbracoPage()' to place control back into the underlying RenderMvcController, and you can pass values by the querystring from the SurfaceController (or viewbag) if needs be.
Or you can RedirectToUmbracoPage() or RedirectToUmbracoUrl() to send the request, once the POST is handled to a GET endpoint that will return a view.
https://our.umbraco.com/documentation/reference/routing/surface-controllers-actions#querystring-parameter-using-a-string-value
So with this proposed pattern of working, say if you had an Advanced Search Form, you might have a SurfaceController handle the POST of the form, validate the values supplied are valid (return CurrentUmbracoPage if not to show validation errors) - then if all valid, Redirect to another Umbraco page, that has a hijacked RenderMvcController to take in the values of the form, passed via Querystring, that then 'does the search' and returns the View/Template that displays the search results...
... but alot of people prefer to have that all in one Controller ...
which is possible (people call this hybrid controllers) if you create a new controller, and make it inherit from SurfaceController AND implement IRenderMvcController
eg people tend to end up with something like this as a base controller to inherit from:
Then this would be able to 'hijack' a RenderMvcController route by convention, and handle a SurfaceController postback 'in the same file'.
Personally I tend to keep them separate, over the years, I just seem to prefer it, when debugging something.
But hopefully that gives you a bit of an insight into what's going on and allows you to form your own approach!
regards
marc
Hi Roy,
@Marc is right, and also you can achieve this by using the following approach
And in view
/Views/BillingAddress.cshtml
form for POSTINGPOST :
Regards Dhanesh
@Marc and @Dhanesh. Thanks, still learning Umbraco so both options are really helpful!
is working on a reply...