(v6) Could not find a Surface controller route error
First to say I have two umbraco installations on my local system, one for the development and one to test the package installation. On both installations the code is working. I have the problem only on the productive system. On every installation the Mvc rendering engine is set. I have not added an own mvc route.
All starts with a macro that calls a partial view:
// Note: The AsynSurfaceController inherits from SurfaceController, is abstract
// and exposes some protected helper methods that are reused among the project.
public class ContactFormAsyncController : AsynSurfaceController
{
[ChildActionOnly]
public ActionResult Index()
{
// Some code
return PartialView("_contactFormAsync", new ContactFormModel());
}
[HttpPost]
public JsonResult SendMail(ContactFormModel model)
{
// Code will not be reached on productive system.
return new JsonResult(...);
}
}
This is the shortened view:
@inherits Umbraco.Web.Mvc.UmbracoViewPage
using (Html.BeginUmbracoForm("SendMail", "ContactFormAsync", null, new Dictionary { { "id", "FormContact" } }))
{
// Some form elements.
Here comes the button (type=button), onclick="sendMail('FormContact', 'ContactContainer')
// the HTML cannot be dislayed here
}
And finally there is some javascript doing an asynchronous postback on the current url (e.g. http://mysite/contact).
var contactViewModel = {
sendMail: function (formId, containerId) {
contactLoadingViewModel.isLoading(true);
var $summaryDiv = $("div#" + containerId + " > .validation-summary-errors");
if ($summaryDiv.length > 0) {
$summaryDiv.remove();
}
var $messageDiv = $("div#" + containerId + " > .contact-error-message");
if ($messageDiv.length > 0) {
$messageDiv.remove();
}
var form = $("#" + formId);
var formData = form.serializeObject();
$.ajax({
dataType: "json",
url: window.location.pathname,
data: formData,
type: "POST",
success: function (response) {
contactLoadingViewModel.isLoading(false);
$('div#' + containerId).replaceWith(response.Html);
},
error: function (response) {
contactLoadingViewModel.isLoading(false);
var $containerDiv = $('div#' + containerId);
$containerDiv.removeClass("contact-val-error");
$containerDiv.prepend("
" + response.getResponseHeader('Message') + "
");
}
});
}
};
As already said on the develoment system everything works fine, the asynchronous postback reaches the controllers SendMail() action and I receive the jscon result on the client.
On the productive system I get the following error:
System.InvalidOperationException: Could not find a Surface controller route in the RouteTable for controller name ContactFormAsync and within the area of umbraco
at Umbraco.Web.Mvc.RenderRouteHandler.HandlePostedValues(RequestContext requestContext, PostedDataProxyInfo postedInfo)
at System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context)
at Umbraco.Web.Routing.PublishedContentRequest.ProcessRequest(HttpContextBase httpContext, UmbracoContext umbracoContext, Action`1 onSuccess)
at System.Web.HttpApplication.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute()
at System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously)
Can someone tell me what I'm missing or what im doing wrong? Thanks for any help.
Thanks for your answer, unfortunately this doesn't help. Changing the controller name and inheriting directly from Umbraco.Web.Mvc.SurfaceController leads to the same error message:
Could not find a Surface controller route in the RouteTable for controller name ContactFormAsyncSurface and within the area of umbraco
Okay I found out something very important. My testsystem had another version, namely v6.0.1-build7 and the productive system has v6.0.1-build18. I upgraded the umbraco installation for the package testing to build18 and now I have the same problem on the test system as well. I'm going to check the newest build as well, and if the error remains I will open a bug on the issue tracker.
Edit: I've reverted the productive system to v6.0.1-build7 and everythring works now as expected!
@Charles: Just for your information. As far I know you don't need the SurfaceController suffix, Controller as suffix is sufficient. As far as see it in the umbraco core code its going after the inheritance and not the name.
Thanks Andreas, just keeping to what the documentation says so i can rule out any problems i have are not to do with not following the documentation :). Thanks. Charlie.
I've tested it using the typed Html.BeginUmbracoForm<T>(...) and that one works!
using (Html.BeginUmbracoForm<MyProject.Umbraco.Controllers.ContactFormAsyncController>("SendMail", null, new Dictionary<string, object> { { "id", "FormContact" } }))
{
// The form
}
At least on my test system. I'm currently not going to touch the other one ;-)
I see that "Html.BeginUmbracoForm" seems to have helped others who've experienced this issue after using the Html.BeginUmbracoForm<T>(...) syntax.
My issue is I'm still unable to postback to the appropriate surface controller even after utilizing this method. It seems that some users were able to resolve the issue by providing the Html.BeginUmbracoForm with some additional arguments, such as new Dictionary<> and etc, but my question is what is the correct syntax structure for using this method?
I'm simply attempting to postback to a surface controller from inside an Umbraco partial macro, and Umbraco cannot find the controller.
I ended up using the following code from an Umbraco macro partial to locate my surface controller (this didn't work until I changed "LoginSurfaceController" to "LoginSurface" for whatever reason):
@Html.Action("Login","LoginSurface")
Then, my surface controller is returning another partial view that contains the following method to create a form that will post-back:
@using (Html.BeginForm())
This enables the form submission to post back to my surface controller, and from there I plan on implementing some validation and redirecting to an appropriate Umbraco page based on validation results.
You stated the following syntax is appropriate for the BeginUmbracoForm:
Controller: The controller class name (without SurfaceController)
This is likely what confused me, because I've been including "SurfaceController" in the call instead of removing that part from my controller's name.
As you already know MVC defines to remove the "Controller" suffix. As Umbraco has its own implementation of a controller that is called "SurfaceController", you always have to remove the suffix "SurfaceController", else the controller cannot be found.
I'm glad you already found your mistake. Happy coding.
Now that my surface controller is accepting post-backs I've encountered another equally frusterating issue. I keep getting the following error when I use the RedirectToCurrentUmbracoPage() method:
"Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"
Additionally, I'm receiving the following error if I attempt to use the RedirectToUmbracoPage(int pageid) method:
"Cannot redirect from a Child Action"
Is this because I'm using an Umbraco Partial Macro to call the surface controller actionresult? I would make the call directly from the template, but the home template I'm using is a master page and doesn't support Razor scripting. I tried to work around this by including the Razor script @Html.Action("Index", "UserLoginSurface") on a Partial Marco included on the Home master template. This causes Umbraco to GET and POST to my desired surface controller, but I can't get anything to redirect back to an Umbraco content page! This is causing my form submission to fail, because I can't redirect the user accordingly.
Hi, where are you using "RedirectToCurrentUmbracoPage" ?
This error: "Cannot redirect from a Child Action" is caused when you try to do any redirect from within rendering a Child Action, because it's 'outer' page is already rendering. The usage of RedirectToCurrentUmbracoPage should be done in your HttpPost of your SurfaceController - not in a ChildAction or Macro.
Perhaps this documentation might help? - depending on your Action names, if you have multiple action names that are the same, have a look for the usage of these attributes in the docs: [ChildActionOnly] and [NotChildAction]
public ActionResult Index(UserLoginViewModel ulvm)
{
return RedirectToCurrentUmbracoPage();
}
Here is the problem. When I include the [NotChildAction] above the ActionResult for Index, Umbraco isn't posting back to that action result. Instead, only the initial child action is executing; this results in only the Get method executing when the form should be posting back. Additionally, when I change the name of the HttpPost ActionResult to lets say "PostIndex" Umbraco is overlooking that ActionResult and is only posting back to the initial Get Index.
Update: So I included [HttpGet] above the intial Index ActionResult and now when Umbraco performs a post-back I receive a message stating:
"A public action method 'Index' was not found on controller 'SetupUmbraco7.Controllers.UserLoginSurfaceController' "
This is a bogus message, because Index obviously exists as an [HttpPost] [NotChildAction] ActionResult in the code I have listed above. If I remove [NotChildAction] the postback works as intended except for the fact that I cannot utilize the RedirectToCurrentUmbracoPage() method and receive the error message: {"Cannot redirect from a Child Action"}
Furthermore, Umbraco is only posting back to an action result entitled "Index" when altering the BeginUmbracoForm HtmlHelper method to the following:
This SHOULD be causing Umbraco to route to the "PostIndex" ActionResult; however, that name is not taken into account for whatever reason and I can only post back when I name the [HttpPost] Method "Index", even if the BeginUmbracoForm references "PostIndex".
First thing to note is that anytime you put this on an action:
[ChildActionOnly]
it becomes not publicly routable - it indicates that this action can only be used for @Html.Action, you cannot just browse (HttpGet) to it therefore combining [HttpGet] and [ChildActionOnly] is probably very confusing the MVC.
A ChildAction is not an HttpGet, it is a controller rendering another controller's action directly.
As I don't know what the rest of your code looks like, I've tested this with this code and it works fine:
Controller and model
namespace MyProj.App_Code
{
public class UserLoginSurfaceController : SurfaceController
{
[ChildActionOnly]
public ActionResult Index()
{
return PartialView("_UserLoginPartial", new UserLoginViewModel());
}
[HttpPost]
[NotChildAction]
public ActionResult Index(UserLoginViewModel ulvm)
{
if (ModelState.IsValid)
{
return RedirectToCurrentUmbracoPage();
}
return CurrentUmbracoPage();
}
}
public class UserLoginViewModel
{
[Required]
public string Name { get; set; }
[Required]
public string Email { get; set; }
}
}
If you type in nothing for either of the fields, you'll get the validation message because when ModelState is invalid we do not redirect (CurrentUmbracoPage). When model state is valid, it will redirect (CurrentUmbracoPage)
Hi Shannon. Thanks for the input. I think we are on the right track at this point; however, there is one distinct difference between our environments.
I'm trying to call the Html.Action for this user login form from my main Home master template. The master template does not support Razor scripting, so I can't actually make a direct call using the general @Html.Action("ActionName","ControllerName") syntax as you did in the code provided above. I tried to work around this by calling an Umbraco Macro Partial from the master template with the following code:
This enables communication between the Umbraco page and my controller, and renders the appropriate partial view from there. However, this seems to be the source of my issue regarding postbacks and child actions. Are you aware of similar issues related to Umbraco Macro Partials? Is there a better way I can call @Html.Action("Index", "UserLoginSurface") from the actual master page template?
So the issue was most certainly related to the Umbraco Macro Partial. When calling the Html.Action from within the partial the @Html.BeginUmbracoForm<T> code (from the partial view I'd return from my ActionResult) would not post back to the appropriate [HttpPost]ActionResult. I am working on rearranging the structure of my content and template pages to enable this feature to work as I originally intended. This will likely involve the use of multiple content placeholders amongsts templates. I'll keep everyone posted upon final resolution.
When you put it like that, it's understandable why this wasn't working! Umbraco was likely confused because this led to a child action within a child action and etc... For the record I managed to get the surface controller to function correctly after:
Associating the Home content page with a new child template, not the ASPX master template.
Making the @Html.Action call from within this child template
Modifying the Partial View returned from within the initial Index ActionResult to associate with an [HttpPost]PostIndex ActionResult
Now I don't know if this was my mistake from the first place or if Umbraco should be able to perform what I was attempting to do, but regardless I'm on the right track now. Thanks for all the help!!!
(v6) Could not find a Surface controller route error
First to say I have two umbraco installations on my local system, one for the development and one to test the package installation. On both installations the code is working. I have the problem only on the productive system. On every installation the Mvc rendering engine is set. I have not added an own mvc route.
All starts with a macro that calls a partial view:
This is the shortened controller:
This is the shortened view:
And finally there is some javascript doing an asynchronous postback on the current url (e.g. http://mysite/contact).
As already said on the develoment system everything works fine, the asynchronous postback reaches the controllers SendMail() action and I receive the jscon result on the client.
On the productive system I get the following error:
Can someone tell me what I'm missing or what im doing wrong? Thanks for any help.
I'm trying to find out whats going on, but I do not find anything..
Running the following code on the productive system
returns the following route to be defined:
So why does this strange error occurs even if the route exists ??
look at the documentation @
http://our.umbraco.org/documentation/master/Reference/Mvc/
http://our.umbraco.org/documentation/master/Reference/Mvc/surface-controllers
Pretty sure you have to suffix your controller with SurfaceController and it has to inherit from Umbraco.Web.Mvc.SurfaceController
Hope this helps. Charlie :)
Thanks for your answer, unfortunately this doesn't help. Changing the controller name and inheriting directly from Umbraco.Web.Mvc.SurfaceController leads to the same error message:
Okay I found out something very important. My testsystem had another version, namely v6.0.1-build7 and the productive system has v6.0.1-build18. I upgraded the umbraco installation for the package testing to build18 and now I have the same problem on the test system as well. I'm going to check the newest build as well, and if the error remains I will open a bug on the issue tracker.
Edit: The error remains for build27...
And here is the bug report:
http://issues.umbraco.org/issue/U4-1773
Edit: I've reverted the productive system to v6.0.1-build7 and everythring works now as expected!
@Charles: Just for your information. As far I know you don't need the SurfaceController suffix, Controller as suffix is sufficient. As far as see it in the umbraco core code its going after the inheritance and not the name.
Thanks Andreas, just keeping to what the documentation says so i can rule out any problems i have are not to do with not following the documentation :). Thanks. Charlie.
There has been a change to the way posted values are handled by SurfaceControllers due to some other issues (http://issues.umbraco.org/issue/U4-1727).
Maybe this change is affecting your code right now.
Hey Floris, thanks for the information about that. I'll try out the strong type Html.BeginUmbracoForm<T> tomorrow, perhaps this will solve the issue.
I've tested it using the typed Html.BeginUmbracoForm<T>(...) and that one works!
At least on my test system. I'm currently not going to touch the other one ;-)
I've updated the issue and is all fixed now. Thanks!
http://issues.umbraco.org/issue/U4-1773
Thanks a lot Shannon. I'm going to test it as soon as possible and will report the results to you! :)
I've just tested it with build30, everything works fine now :)
I see that "Html.BeginUmbracoForm" seems to have helped others who've experienced this issue after using the Html.BeginUmbracoForm<T>(...) syntax.
My issue is I'm still unable to postback to the appropriate surface controller even after utilizing this method. It seems that some users were able to resolve the issue by providing the Html.BeginUmbracoForm with some additional arguments, such as new Dictionary<> and etc, but my question is what is the correct syntax structure for using this method?
I'm simply attempting to postback to a surface controller from inside an Umbraco partial macro, and Umbraco cannot find the controller.
Im doing the following:
Where:
Hi Andreas.
I think Umbraco's routing system may have attributed to my issue. I provided details of my resolution in the following thread:
http://our.umbraco.org/forum/umbraco-as-a-service/issues/47413-Razor-syntax-is-not-being-recognized-from-the-Edit-template-page
I ended up using the following code from an Umbraco macro partial to locate my surface controller (this didn't work until I changed "LoginSurfaceController" to "LoginSurface" for whatever reason):
@Html.Action("Login","LoginSurface")
Then, my surface controller is returning another partial view that contains the following method to create a form that will post-back:
@using (Html.BeginForm())
This enables the form submission to post back to my surface controller, and from there I plan on implementing some validation and redirecting to an appropriate Umbraco page based on validation results.
You stated the following syntax is appropriate for the BeginUmbracoForm:
As you already know MVC defines to remove the "Controller" suffix. As Umbraco has its own implementation of a controller that is called "SurfaceController", you always have to remove the suffix "SurfaceController", else the controller cannot be found.
I'm glad you already found your mistake. Happy coding.
Thanks, Andreas.
Now that my surface controller is accepting post-backs I've encountered another equally frusterating issue. I keep getting the following error when I use the RedirectToCurrentUmbracoPage() method:
"Cannot find the Umbraco route definition in the route values, the request must be made in the context of an Umbraco request"
Additionally, I'm receiving the following error if I attempt to use the RedirectToUmbracoPage(int pageid) method:
"Cannot redirect from a Child Action"
Is this because I'm using an Umbraco Partial Macro to call the surface controller actionresult? I would make the call directly from the template, but the home template I'm using is a master page and doesn't support Razor scripting. I tried to work around this by including the Razor script @Html.Action("Index", "UserLoginSurface") on a Partial Marco included on the Home master template. This causes Umbraco to GET and POST to my desired surface controller, but I can't get anything to redirect back to an Umbraco content page! This is causing my form submission to fail, because I can't redirect the user accordingly.
Hi, where are you using "RedirectToCurrentUmbracoPage" ?
This error: "Cannot redirect from a Child Action" is caused when you try to do any redirect from within rendering a Child Action, because it's 'outer' page is already rendering. The usage of RedirectToCurrentUmbracoPage should be done in your HttpPost of your SurfaceController - not in a ChildAction or Macro.
Perhaps this documentation might help? - depending on your Action names, if you have multiple action names that are the same, have a look for the usage of these attributes in the docs: [ChildActionOnly] and [NotChildAction]
[ChildActionOnly]
public ActionResult Index()
{
return PartialView("_UserLoginPartial",new UserLoginViewModel());
}
[HttpPost]
[NotChildAction]
public ActionResult Index(UserLoginViewModel ulvm)
{
return RedirectToCurrentUmbracoPage();
}
Here is the problem. When I include the [NotChildAction] above the ActionResult for Index, Umbraco isn't posting back to that action result. Instead, only the initial child action is executing; this results in only the Get method executing when the form should be posting back. Additionally, when I change the name of the HttpPost ActionResult to lets say "PostIndex" Umbraco is overlooking that ActionResult and is only posting back to the initial Get Index.
Update: So I included [HttpGet] above the intial Index ActionResult and now when Umbraco performs a post-back I receive a message stating:
"A public action method 'Index' was not found on controller 'SetupUmbraco7.Controllers.UserLoginSurfaceController' "
This is a bogus message, because Index obviously exists as an [HttpPost] [NotChildAction] ActionResult in the code I have listed above. If I remove [NotChildAction] the postback works as intended except for the fact that I cannot utilize the RedirectToCurrentUmbracoPage() method and receive the error message: {"Cannot redirect from a Child Action"}
Furthermore, Umbraco is only posting back to an action result entitled "Index" when altering the BeginUmbracoForm HtmlHelper method to the following:
@using (Html.BeginUmbracoForm<SetupUmbraco7.Controllers.UserLoginSurfaceController>("PostIndex"))
{
//form data goes here
}
This SHOULD be causing Umbraco to route to the "PostIndex" ActionResult; however, that name is not taken into account for whatever reason and I can only post back when I name the [HttpPost] Method "Index", even if the BeginUmbracoForm references "PostIndex".
Why?
First thing to note is that anytime you put this on an action:
[ChildActionOnly]
it becomes not publicly routable - it indicates that this action can only be used for @Html.Action, you cannot just browse (HttpGet) to it therefore combining [HttpGet] and [ChildActionOnly] is probably very confusing the MVC.
A ChildAction is not an HttpGet, it is a controller rendering another controller's action directly.
As I don't know what the rest of your code looks like, I've tested this with this code and it works fine:
Controller and model
Umbraco Template
Partial View (_UserLoginPartial)
If you type in nothing for either of the fields, you'll get the validation message because when ModelState is invalid we do not redirect (CurrentUmbracoPage). When model state is valid, it will redirect (CurrentUmbracoPage)
Hi Shannon. Thanks for the input. I think we are on the right track at this point; however, there is one distinct difference between our environments.
I'm trying to call the Html.Action for this user login form from my main Home master template. The master template does not support Razor scripting, so I can't actually make a direct call using the general @Html.Action("ActionName","ControllerName") syntax as you did in the code provided above. I tried to work around this by calling an Umbraco Macro Partial from the master template with the following code:
<umbraco:macro alias="LoginPartialMacro" runat="server"/>
Then, the LoginPartialMacro looks like this:
@inherits Umbraco.Web.Macros.PartialViewMacroPage
@Html.Action("Index", "UserLoginSurface")
This enables communication between the Umbraco page and my controller, and renders the appropriate partial view from there. However, this seems to be the source of my issue regarding postbacks and child actions. Are you aware of similar issues related to Umbraco Macro Partials? Is there a better way I can call @Html.Action("Index", "UserLoginSurface") from the actual master page template?
So the issue was most certainly related to the Umbraco Macro Partial. When calling the Html.Action from within the partial the @Html.BeginUmbracoForm<T> code (from the partial view I'd return from my ActionResult) would not post back to the appropriate [HttpPost]ActionResult. I am working on rearranging the structure of my content and template pages to enable this feature to work as I originally intended. This will likely involve the use of multiple content placeholders amongsts templates. I'll keep everyone posted upon final resolution.
@Jacob, what you are trying to do is fairly interesting:
I will try this out and see what happens
@Shannon,
When you put it like that, it's understandable why this wasn't working! Umbraco was likely confused because this led to a child action within a child action and etc... For the record I managed to get the surface controller to function correctly after:
@Jacob, I'll hopefully have time to find out today. I'm not sure I've actually attempted to do that before ;)
is working on a reply...