Getting an error with custom routes: Could not find a Surface controller route in the RouteTable for controller name Authorization
I'm getting an error on my 7.2.8 Umbraco instance:
"Could not find a Surface controller route in the RouteTable for controller name Authorization"
I have a custom route defined:
routes.MapUmbracoRoute(
"auth",
firstUrlSegment + "/{action}",
new
{
controller = "Authorization",
action = "Index",
},
new PortalAuthorizationRouteHandler());
The PortalAuthorizationRouteHandler inherits from UmbracoVirtualNodeRouteHandler and implements FindContent and that's all it does:
public class PortalAuthorizationRouteHandler : UmbracoVirtualNodeRouteHandler
{
protected override Umbraco.Core.Models.IPublishedContent FindContent(System.Web.Routing.RequestContext requestContext, Umbraco.Web.UmbracoContext umbracoContext)
{
var settingsnodeid = int.Parse(WebConfigurationManager.AppSettings["Portal:SettingsNode"]);
var settings = umbracoContext.ContentCache.GetById(settingsnodeid);
if (settings == null) return null;
var actionName = "login";
var routeValues = requestContext.RouteData.Values;
if (routeValues != null)
{
if (routeValues.ContainsKey("action"))
{
actionName = requestContext.RouteData.Values["action"].ToString();
}
}
var propertyAlias = "";
switch (actionName.ToLower())
{
case "index":
case "login":
propertyAlias = "login";
break;
case "forgottenpassword":
propertyAlias = "forgotpassword";
break;
case "registration":
propertyAlias = "registration";
break;
case "resetpassword":
propertyAlias = "forgotpassword";
break;
}
var content = settings.GetPropertyValue<IPublishedContent>(propertyAlias);
if (content == null) return null;
return content;
}
}
My controller gets called properly on the initial "Get" of the page:
[HttpGet]
public ActionResult ForgottenPassword()
{
var model = BuildModel<AuthorizationForgotPasswordFormModel>();
return View(model);
}
But when I try to "Post" the form, I get the error:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult ForgottenPassword(AuthorizationForgotPasswordFormModel model)
{
if (ModelState.IsValid)
{
... (Never gets to this action)
}
}
My view is using UmbracoBeginForm and is passing in a specific controller to handle the POST:
I'm not sure why the error is occurring. Here's the stack trace:
[InvalidOperationException: Could not find a Surface controller route in the RouteTable for controller name Authorization]
Umbraco.Web.Mvc.RenderRouteHandler.HandlePostedValues(RequestContext requestContext, PostedDataProxyInfo postedInfo) +622
Umbraco.Web.Mvc.UmbracoVirtualNodeRouteHandler.GetHttpHandler(RequestContext requestContext) +618
System.Web.Routing.UrlRoutingModule.PostResolveRequestCache(HttpContextBase context) +12411255
System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +92
System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +165
I am using the Hybrid methodology with a RenderModel and the base SurfaceController that my AuthoizationController inherits from inherits from "SurfaceController, IRenderMvcController"
Anyone have any ideas? I do get a breakpoint to hit in my PortalAuthorizationRouteHandler FindContent() method and the controller and action are correct. I never get a breakpoint hit in my controller action method because the error hits first.
I don't suppose you have any other routes defined do you? I imagine it's an 'Umbraco' thing. But I had the same sort of problem with a Sitecore project today
routes.MapUmbracoRoute(
"authResetPassword",
firstUrlSegment + "/reset-password/{token}/{memberId}",
new
{
controller = "Authorization",
action = "ResetPassword",
},
new UmbracoVirtualNodeByIdRouteHandler(nodeid));
My model inherits from an Umbraco.Web.Models.RenderModel. I was wondering if that is the issue, but I can't seem to get it to post to a plain POCO model even with just using plain old BeginForm...
No worries :). I remember having the same sort of problem. Isn't there something else you can inherit from in the model or the controller. What model do you have in your view? What type is it? I have a hunch it's your parameters, not sure why
There are three surfaceRoutes in the collection -- my two I listed above, plus one for:
"umbraco/Surface/Authorization/{action}/{id}"
Since the code isn't sure what to do it checks against the Defaults to try to match that route. Since none of my defaults match that Action, it returns null and throws the error.
routes.MapUmbracoRoute(
"authForgottenPassword",
firstUrlSegment + "/{action}",
new
{
controller = "Authorization",
action = "ForgottenPassword",
},
new PortalAuthorizationRouteHandler());
Now I'm getting a new error. Apparently, the UmbracoVirtualNodeRouteHandler GetHttpHandler() method is getting called twice and the DataTokens are being added to twice which causes an exception that the key already exists.
With the ignoreroute. There is an overload that takes a collection (that are allow route paths) You could ignore the Umbraco route and pass in your routes as parameters. ? :)
Tried a quick fix to simply not add the keys if they already exist (in the Umbraco.Web project) and pushed that to my site. Now I'm getting this error:
Recursive read lock acquisitions not allowed in this mode.
Stack Trace:
When I did that, I didn't need to check for duplicates in the UmbracoVirtualNodeRouteHandler GetHttpHandler() method because its no longer called twice.
So, one little change to the core makes it all work.
Hi, I will look at this more tomorrow but here are some tips
don't mix surface controllers with render controllers
Don't mix surface controllers with custom routed controllers
surface controllers are auto routed, so they already have a route, so now you have multiple routes for the same controller
if you are using custom routes, you are using mvc, plain old mvc, so you can just use mvc the normal way, you don't even need surface controllers to post to, just pass in the current node id to your custom, non-surface controller form. You can still use surface controllers of you really want but since you're creating your own routes there is no need to. Surface controllers are made for form post back when inside the Umbraco routing pipeline, you are operating outside this pipeline, so you are just doing mvc.
I just saw your details on the tracker.... Before you submit a PR for that change, I suspect it might break normal surface controller post back behavior, it might work for this one instance but have a feeling it will break all other cases. I'm any case happy to review more tomorrow and if you could test your proposed change with all normal scenarios that'd be good as well as testing your implementation with the above recommendations.
Getting an error with custom routes: Could not find a Surface controller route in the RouteTable for controller name Authorization
I'm getting an error on my 7.2.8 Umbraco instance:
"Could not find a Surface controller route in the RouteTable for controller name Authorization"
I have a custom route defined:
The PortalAuthorizationRouteHandler inherits from UmbracoVirtualNodeRouteHandler and implements FindContent and that's all it does:
My controller gets called properly on the initial "Get" of the page:
But when I try to "Post" the form, I get the error:
My view is using UmbracoBeginForm and is passing in a specific controller to handle the POST:
I'm not sure why the error is occurring. Here's the stack trace:
I am using the Hybrid methodology with a RenderModel and the base SurfaceController that my AuthoizationController inherits from inherits from "SurfaceController, IRenderMvcController"
Anyone have any ideas? I do get a breakpoint to hit in my PortalAuthorizationRouteHandler FindContent() method and the controller and action are correct. I never get a breakpoint hit in my controller action method because the error hits first.
Thanks, Jason
Hello :)
I don't suppose you have any other routes defined do you? I imagine it's an 'Umbraco' thing. But I had the same sort of problem with a Sitecore project today
What is the model type you are passing from the view? It could be because the type or name of the controller is wrong?
I do have one other route:
My model inherits from an Umbraco.Web.Models.RenderModel. I was wondering if that is the issue, but I can't seem to get it to post to a plain POCO model even with just using plain old BeginForm...
Still trying things.
Thanks for the responses!
No worries :). I remember having the same sort of problem. Isn't there something else you can inherit from in the model or the controller. What model do you have in your view? What type is it? I have a hunch it's your parameters, not sure why
I'm debugging the Umbraco Source and it fails here:
https://github.com/umbraco/Umbraco-CMS/blob/7.2.8/src/Umbraco.Web/Mvc/RenderRouteHandler.cs#L231
There are three surfaceRoutes in the collection -- my two I listed above, plus one for:
"umbraco/Surface/Authorization/{action}/{id}"
Since the code isn't sure what to do it checks against the Defaults to try to match that route. Since none of my defaults match that Action, it returns null and throws the error.
Now what to do about that...
Tried adding a new route:
Now I'm getting a new error. Apparently, the UmbracoVirtualNodeRouteHandler GetHttpHandler() method is getting called twice and the DataTokens are being added to twice which causes an exception that the key already exists.
https://github.com/umbraco/Umbraco-CMS/blob/7.2.8/src/Umbraco.Web/Mvc/UmbracoVirtualNodeRouteHandler.cs#L34
Trying so see now if there's a workaround or if I need to file a bug on the issues.
Do you need that route?
With the ignoreroute. There is an overload that takes a collection (that are allow route paths) You could ignore the Umbraco route and pass in your routes as parameters. ? :)
I cannot override that method... I think the only option is to tweak the core. I'll need to get Shannon's eye's on this.
I'll create an issue on YouTrack
Tried a quick fix to simply not add the keys if they already exist (in the Umbraco.Web project) and pushed that to my site. Now I'm getting this error:
Recursive read lock acquisitions not allowed in this mode. Stack Trace:
I may be hitting a wall here...
I edited one more thing in the core:
I changed this line:
(https://github.com/umbraco/Umbraco-CMS/blob/7.2.8/src/Umbraco.Web/Mvc/RenderRouteHandler.cs#L266)
TO:
When I did that, I didn't need to check for duplicates in the UmbracoVirtualNodeRouteHandler GetHttpHandler() method because its no longer called twice.
So, one little change to the core makes it all work.
Heading to YouTrack now...
Hi, I will look at this more tomorrow but here are some tips
Another tip, is if you are creating custom routes, be as specific with the route as possible, for example, the route you mentioned
routes.MapUmbracoRoute( "authForgottenPassword", firstUrlSegment + "/{action}", new { controller = "Authorization", action = "ForgottenPassword", }, new PortalAuthorizationRouteHandler());
If there's actually only one endpoint which is
firstUrlSegment + "ForgottenPassword"
Then it should just be that exact route without the {action} token
I just saw your details on the tracker.... Before you submit a PR for that change, I suspect it might break normal surface controller post back behavior, it might work for this one instance but have a feeling it will break all other cases. I'm any case happy to review more tomorrow and if you could test your proposed change with all normal scenarios that'd be good as well as testing your implementation with the above recommendations.
If there's another way around this, then I would love to do it. Are you saying having two controllers? One for the render and one for the post?
Or if there are things I should test to QA the change, let me know.
I'm available for a Skype if needed. I could do it fairly early here I think. Which would be late in the day for you.
Also, thanks for the tip on the more specific url segment.
is working on a reply...