Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Andreas Iseli 150 posts 427 karma points
    Feb 22, 2013 @ 18:03
    Andreas Iseli
    0

    (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:

    @inherits Umbraco.Web.Macros.PartialViewMacroPage
    
    @{
        Html.RenderAction("Index", "ContactFormAsync");
    }

    This is the shortened controller:

    // 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.

  • Andreas Iseli 150 posts 427 karma points
    Feb 23, 2013 @ 13:32
    Andreas Iseli
    0

    I'm trying to find out whats going on, but I do not find anything..

    Running the following code on the productive system

    @foreach (var route in RouteTable.Routes.Cast())
    {
    @route.Url
    }

    returns the following route to be defined:

    umbraco/Surface/ContactFormAsync/{action}/{id}

    So why does this strange error occurs even if the route exists ??

  • Charles Afford 1163 posts 1709 karma points
    Feb 24, 2013 @ 13:49
    Charles Afford
    0

    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 :) 

  • Andreas Iseli 150 posts 427 karma points
    Feb 24, 2013 @ 15:59
    Andreas Iseli
    0

    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
  • Andreas Iseli 150 posts 427 karma points
    Feb 24, 2013 @ 16:50
    Andreas Iseli
    0

    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...

  • Andreas Iseli 150 posts 427 karma points
    Feb 24, 2013 @ 16:56
    Andreas Iseli
    0

    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.

  • Charles Afford 1163 posts 1709 karma points
    Feb 24, 2013 @ 19:15
    Charles Afford
    0

    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.

  • Floris Robbemont 57 posts 89 karma points c-trib
    Feb 24, 2013 @ 19:44
    Floris Robbemont
    0

    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. 

  • Andreas Iseli 150 posts 427 karma points
    Feb 24, 2013 @ 20:16
    Andreas Iseli
    0

    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.

  • Andreas Iseli 150 posts 427 karma points
    Feb 25, 2013 @ 08:18
    Andreas Iseli
    1

    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 ;-)

  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Feb 25, 2013 @ 15:19
    Shannon Deminick
    0

    I've updated the issue and is all fixed now. Thanks!

    http://issues.umbraco.org/issue/U4-1773

  • Andreas Iseli 150 posts 427 karma points
    Feb 25, 2013 @ 17:09
    Andreas Iseli
    0

    Thanks a lot Shannon. I'm going to test it as soon as possible and will report the results to you! :)

  • Andreas Iseli 150 posts 427 karma points
    Feb 25, 2013 @ 20:13
    Andreas Iseli
    0

    I've just tested it with build30, everything works fine now :)

  • Jacob 41 posts 90 karma points
    Jan 13, 2014 @ 17:56
    Jacob
    0

    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.

  • Andreas Iseli 150 posts 427 karma points
    Jan 15, 2014 @ 08:30
    Andreas Iseli
    0

    Im doing the following:

    using (Html.BeginUmbracoForm("Action", "Controller", null, new Dictionary<string, object> { { "id", "FormId" }, { "data-bind", "Display: isLoading() !== true" } }))

    Where:

    • Action: The Action Method of the controller
    • Controller: The controller class name (without SurfaceController)
    • new Dictionary: The values to append to the created form; at least the ID ist required, the data-bind relates to knockout JS functionalities
    Actually I'm doing the postpack using an AJAX request and then I replace the content of a div. How does your BeingUmbracoForm call looks like?
  • Jacob 41 posts 90 karma points
    Jan 15, 2014 @ 14:52
    Jacob
    0

    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: 

    • 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.
  • Andreas Iseli 150 posts 427 karma points
    Jan 15, 2014 @ 21:52
    Andreas Iseli
    0

    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.

  • Jacob 41 posts 90 karma points
    Jan 15, 2014 @ 22:25
    Jacob
    0

    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.

  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Jan 16, 2014 @ 01:58
    Shannon Deminick
    0

    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]

  • Jacob 41 posts 90 karma points
    Jan 17, 2014 @ 15:41
    Jacob
    0

            [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?

  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Jan 22, 2014 @ 00:54
    Shannon Deminick
    1

    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; }
        }
    }
    

    Umbraco Template

    @inherits Umbraco.Web.Mvc.UmbracoTemplatePage
    @{
        Layout = null;
    }
    <html>
    <body>
        <h1>Hello</h1>
        @(Html.Action("Index", "UserLoginSurface"))
    </body>
    </html>
    

    Partial View (_UserLoginPartial)

    @model Umbraco.Web.UI.App_Code.UserLoginViewModel
    
    @using (Html.BeginUmbracoForm<MyProj.App_Code.UserLoginSurfaceController>("Index"))
    {
        <div class="formRow">
            @Html.LabelFor(x => Model.Name)
            @Html.EditorFor(x => Model.Name)
            @Html.ValidationMessageFor(x => Model.Name)
        </div>
    
        <div class="formRow">
            @Html.LabelFor(x => Model.Email)
            @Html.EditorFor(x => Model.Email)
            @Html.ValidationMessageFor(x => Model.Email)
        </div>
    
    
        <div class="buttonRow">
            <input type="submit" name="Submit" />
        </div>
    }
    

    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)

  • Jacob 41 posts 90 karma points
    Jan 22, 2014 @ 15:14
    Jacob
    0

    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?

  • Jacob 41 posts 90 karma points
    Jan 22, 2014 @ 21:30
    Jacob
    0

    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.

  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Jan 22, 2014 @ 23:09
    Shannon Deminick
    0

    @Jacob, what you are trying to do is fairly interesting:

    • Rendering a Webforms master page view
    • This renders a Partial View Macro - internally this renders as an MVC ChildAction
    • Inside this Partial View Macro you are rendering another ChildAction

    I will try this out and see what happens

  • Jacob 41 posts 90 karma points
    Jan 22, 2014 @ 23:26
    Jacob
    0

    @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:

    • 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!!!
  • Shannon Deminick 1524 posts 5269 karma points MVP 2x
    Jan 23, 2014 @ 00:07
    Shannon Deminick
    0

    @Jacob, I'll hopefully have time to find out today. I'm not sure I've actually attempted to do that before ;)

Please Sign in or register to post replies

Write your reply to:

Draft