Kendo MVC Helper "Route()" Generates Wrong URL - kendo-ui

I'm trying to define a custom Kendo grid toolbar command, using the Kendo Route() MVC helper as in
#(Html.Kendo().Grid(Model.Imports)
.Name("ImportsGrid")
.ToolBar(tb => tb.Custom().Route("DataImportData", new { tableType = DataTablesTypeEnum.MyTables, id = Model.TableName }).Text("Import Data"))
But the generated URL is incorrect - it ends up being the same URL as the page that contains the grid which is a clue that it couldn't find the route somehow.
However... When I use the Url.RouteUrl() method like so
#{ var url = Url.RouteUrl("DataImportData", new { tableType = DataTablesTypeEnum.MyTables, id = Model.TableName });}
#(Html.Kendo().Grid(Model.Imports)
.Name("ImportsGrid")
.ToolBar(tb => tb.Custom().Url(url).Text("Import Data"))
The correct URL is generated.
My route is defined like this:
routes.MapRoute(
name: "DataImportData",
url: "{controller}/{tableType}/{id}/Import",
defaults: new { controller = "Data", action = "ImportData" },
namespaces: new[] { "MyApp.MyNamespace" }
);
Is this a known problem with Kendo or am I doing something wrong?

It turns out that the problem is because the action is specified as a default and not as a parameter. If I include the action in the route data the Kendo Route() method, the route is properly matched.
.ToolBar(tb => tb.Custom().Route("DataImportData", new { action="ImportData", tableType = DataTableTypeEnum.MyTables, id = Model.TableName }).Text("Import Data"))
I also discovered that, at least in this case, I can simply use the Action method and specify the action ("ImportData") directly as in
.ToolBar(tb => tb.Custom().Action("ImportData", "Data", new { tableType = DataTableTypeEnum.MyTables, id = Model.TableName }).Text("Import Data"))
and the route is properly matched with the expected URL.

Related

Can't map route to action mvc3

I'm trying to create a new Route in MVC3 to achieve the link http://localhost/Product/1/abcxyz:
routes.MapRoute(
"ProductIndex", // Route name
"{controller}/{id}/{name}", // URL with parameters
new { controller = "Product", action = "Index", id = UrlParameter.Optional, name = UrlParameter.Optional } // Parameter defaults
);
And I used Route Link like this:
<li>#Html.RouteLink("My Link", "ProductIndex", new { controller = "Product", id = 10, name = "abcxyz" })</li>
Product Index action:
public ViewResult Index(int id, string name)
{
var product = db.Product.Include(t => t.SubCategory).Where(s => s.SubID == id);
return View(product.ToList());
}
The url render as I expected. But when I click on it, I got a 404 error with message
HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable. Please review the following URL and make sure that it is spelled correctly
UPDATE
I place that Route above Default Route and the URL work fine. But there's a problem occure. My index page http://locahost point directly to Index action of Product controller, but I want it points to Index action of Home controller instead
It's because you have 2 optional parameters in your route and the engine can't work out which one to set the value to. See my answer to a similar issue here
You can create a specific route for your products controller first (with mandatory id) and then have the generic fallback route afterwards.
routes.MapRoute(
"ProductIndex", // Route name
"products/{id}/{name}", // URL with parameters
new { controller = "Product", action = "Index", name = UrlParameter.Optional } // Parameter defaults
);
Try it
routes.MapRoute(
"Default", // Route name
"{controller}/{id}/{name}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, name = UrlParameter.Optional } // Parameter defaults
);
for routing details see this link. In this link every type of routing is discussed.
http://www.codeproject.com/Articles/408227/Routing-in-MVC3

Url.Action does not give the expected result. Unwanted route values remain

I have a MVC3 app with several routes. Two of them are defined like this:
routes.MapRoute(
null,
"System/{name}", // URL with parameters
new { controller = "Systems", action = "Index" } // Parameter defaults
);
routes.MapRoute(
null,
"Carrier/{name}", // URL with parameters
new { controller = "Carriers", action = "Index" } // Parameter defaults
);
Now, in my menu I have two links to these routes that are created using Url.Action:
Url.Action("Index","Systems")
Url.Action("Index","Carriers")
Now when i start the application, everything seems fine, and the links in the menu show as /System/ and /Carrier/, and that is the expected value.
However, when i browse to for example /System/MySystem in the web page i would still want the links to point to the same place, but now they point to /System/MySystem and /Carrier/MySystem.
I have tried lots of things to keep the link from using the name from the route value, but to no avail. The strangest case i experienced was when i tried this:
Url.Action("Index","Systems", new{name = (string)null})
Now the link showed up as
/System?name=MySystem
Are there any good way here to make sure that the name value from the route values does not interfer with these links in any way?
As you noticed the Url. helpers reuse the previously given route parameters.
As a workaround (I hope that there is a more elegant solution...) you can remove the name entry from the RouteData.Values in your view:
So before calling you Url.Action in your view:
Url.Action("Index","Systems")
Url.Action("Index","Carriers")
Remove the prefilled name from the RequestContext:
#{
Request.RequestContext.RouteData.Values.Remove("name");
}
It's also an workaround but if you slightly modify your routes with providing a default null value for your name segment:
routes.MapRoute(
null,
"System/{name}", // URL with parameters
new { controller = "Systems", action = "Index", name = (string)null }
);
routes.MapRoute(
null,
"Carrier/{name}", // URL with parameters
new { controller = "Carriers", action = "Index", name = (string)null }
);
Your original solution ("nulling" the name in the Url.Action) will also work :
#Url.Action("Index", "Systems" , new {name = (string)null} )

Routing legacy requests with and without querystring

(Before starting: I am aware of this and this. I'd like to find a more concise solution -if possible- for a slightly more specific problem)
I'm rewriting an old Webforms app in MVC. As usual, no permalinks should be broken.
I'm using standard {controller}/{action}/{id} routes. Legacy paths are usually SomePage.aspx?ID=xxx, and I have one particular case where Foo.aspx is a list of Bar (new URL: /Bar or /Bar/Index) and
Foo.aspx?ID=xxx is the Bar detail (new URL: /Bar/View/xxx)
One possible workaround is adding the following before the Default MapRoute:
routes.MapRoute("Bar View", "Foo.aspx",
new { controller = "Bar", action = "View" });
And then defining the corresponding action in BarController:
public ActionResult View(int? id)
{
if (id == null)
return RedirectToAction("Index");
return View();
}
There are two problems with this:
Now, if I create an ActionLink, it uses the legacy format
I'd like to handle this in the routes; making the id nullable and redirecting in the controller is just wrong
I'm fine with mapping the legacy URLs by hand (I don't need a generic solution and there are only about 8 pages)
This is a new project, so I'm not tied to anything.
I was able to solve this based on Dangerous' idea plus a constraint based on this answer.
My new route table is:
routes.MapRoute("Bar", "Bar/{action}/{id}",
new
{
controller = "Bar",
action = "Index",
id = UrlParameter.Optional
});
routes.MapRoute("Bar View", "Foo.aspx",
new {controller = "Bar", action = "View"},
new {id = new QueryStringConstraint()});
routes.MapRoute("Bar Index", "Foo.aspx",
new { controller = "Bar", action = "Index" });
routes.MapRoute("Default", /*...*/);
And the QueryStringConstraint couldn't be simpler:
public class QueryStringConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route,
string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
return httpContext.Request.QueryString.AllKeys
.Contains(parameterName, StringComparer.InvariantCultureIgnoreCase);
}
}
I believe if you specify the following routes:
routes.MapRoute(
null,
"Bar/{action}/{id}",
new { controller = "Bar", action = "View", id = UrlParameter.Optional },
new { action = "Index|Next" } //contrain route from being used by other action (if required)
);
routes.MapRoute(
null,
"Foo.aspx/{id}",
new { controller = "Bar", action = "View", id = UrlParameter.Optional }
);
//specify other routes here for the other legacy routes you have.
Then this should solve your first problem. If the user specifies Foo.aspx in the url then they will be taken to the View action.
If the action link:
#Html.ActionLink("Click me", "Index", "Bar")
is specified then the first route will be used (as the order matters).
However, I could not figure out how to specify if Foo.aspx?id=... then to go to one route else if Foo.aspx is specified then go to the other route. Therefore, I would check whether id is null in the action. However, if you do find this out I would very much like to know.
Hope this helps.

I can't get the #ActionLink to render the correct url to work with my MVC3 default routing

I'm trying to take advantage of the default routing so I get a URL without a query string parameter.
So, I've currently got this url:
http://www.mysite.Items/Edit?ItemID=19719
And I'm trying to get a URL like this:
http://www.mysite.Items/Edit/19719
The routing works, but I can't get the #Html.ActionLink method to produce the 2nd url.
Here is my razor code:
#Html.ActionLink("Edit", "Edit", new {item.ItemID}, new { id = "edit-" + item.ItemID })
The first argument is my link's text. The 2nd argument is the Action. 3rd is the ID Value and finally the last argument is and HTML attribute I use for some javascript I'm using.
Originally I had my 3rd Argument as
new {itemID = itemID}
This was when my Edit action expected an integer value named itemID as the parameter. I changed it to 'id' so the routing would work.
Ideally I would like a route that would pass the 19719 value to an action with the argument named itemID, but this is beyond the scope of this question.
Thanks in advance.
SOLVED
Thanks Darin Dimitrov for this solution.
I ended up leaving my html code and action arguments the way I had them originally. All that was really required was an update to my routes. I should note that I had to add my new route map before the default. Anyway, here is my route registration now that made this all work.
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute("", "Items/{action}/{itemID}", new { controller = "Items", action = "Details", itemID = #"\d+" });
routes.MapRoute("Default", "{controller}/{action}/{id}", new { controller = "Home", action = "Index", id = UrlParameter.Optional } );
}
The default routes uses id as route token name, so you want:
#Html.ActionLink(
"Edit",
"Edit",
new { id = item.ItemID },
new { id = "edit-" + item.ItemID }
)
Notice new { id = item.ItemID } and not new {itemID = itemID} and not new {item.ItemID}.

ASP.net MVC RenderAction and Route

I have a web project using ASP.net MVC3. There's an child action in my project. I use
<% Html.RenderAction("Navigator", "Application");%>
to call a shared action. But I find that if my current url is "localhost/application", it throws an exception "No route in the route table matches the supplied values". But when current url is "localhost/application/index", it works fine. Index is a default action in my route config, which is shown below:
public static void RegisterRoutesTo(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.IgnoreRoute("{*favicon}", new { favicon = #"(.*/)?favicon.ico(/.*)?" });
//routes.IgnoreRoute("{*chartName}", new { chartName = #"(.*)?Chart.aspx" }); //ignore request for charting request
routes.Ignore("{*pathInfo}", new { pathInfo = #"^.*(ChartImg.axd)$" });
routes.MapRoute(
"Default", // Route name
"{controller}/{id}/{action}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }, // Parameter defaults
new { httpMethod = new HttpMethodConstraint("GET", "POST") } // Allowed http methods
);
}
Note that I switch default id and action position. I see mvc can remove the default controller and action name in url when using "Html.ActionLink(...)". And I don't like to use explicit url string in my views. How can make it work?
My Action code is simple:
[ChildActionOnly]
public ActionResult Navigator()
{
return PartialView(appFacility.GetAll());
}
Thanks alot.
Wrong route URL definition and defaults combination
The problem is that you can't have non optional parameters after an optional parameter.
Why does localhost/application/index work? This are route values:
controller = "application" (supplied from URL)
id = "index" (supplied from URL)
action = "Index" (supplied as route default)
Basically these values equal to localhost/application/index/index request URL.
If you'd like your RenderAction to work, you'd have to call it this way:
<% Html.RenderAction("Navigator", "Application", new { id = 0 }); %>
which would equal to localhost/application/0/navigator request URL.
But you'll soon find out that your route doesn't work and you'll have to change it (because I suppose you don't like having that additional 0 in your URL). If you provide information how you'd like your route work (or why you've decided to switch action and id) we can provide an answer that will help you meet your requirements.
Optinal parameters work correctly only on the end of route. Try something like this:
routes.MapRoute("DefaultWithID", "{controller}/{id}/{action}",
new { action = "Index" },
new { id = "^[0-9]+$" }
);
routes.MapRoute("Default", "{controller}/{action}",
new { controller = "Home", action = "Index" }
);
edit: hopefully fixed :) this version counts on fact that ID will be numeric - without constraint we can't tell whether it would mean action or id, so there couldn't be default action on routes when ID is specified

Resources