How can I pass the URL to the controller while routing ignores it? - asp.net-mvc

Is there a way within asp.net MVC 2 whereby I can route a request and have a portion of the URL ignored and passed to the controller as a variable?
My needs state that I must store pages dynamically in a database, and they should be accessible by looking at the URL and reading the URL segments to find the relevant page. Effectively, I need a Site controller, to which the remaining portion of the URL will be passed.
Site-Controller/this/is/a/page
So this in case the site controller would pick up the /this/is/a/page 'string'
Is this possible?
Thanks!

Yes, use a wildcard route, like:
routes.MapRoute(
"SiteController", // Route name
"Site-Controller/{*url}", // URL with parameters
new { controller = "SiteController", action = "Index" }, // Parameter defaults
null // constraints
);
Then your action looks like:
public ActionResult Index(string url)
{
}

Create a wildcard Route in Global.asax which captures everything after the first segment of the url and passes it to your Action method:
routes.MapRoute("Page",
"Site-Controller/{*urlsegments}",
new {
controller = "Site-Controller",
action = "YourAction",
urlsegments = ""
});
Make sure your Action method accepts a 'urlsegments' parameter and you can work with it from there:
public ActionResult YourAction(string urlsegments)
{
// Do something with the segments here
}

Related

If action name found then call action, if not call a default action in MVC Routing

this is my current routing
routes.MapRoute(
"Default", // Route name
"{action}/{id}", // URL with parameters
new { controller = "MarketingSite", action = "index", id = "" } // Parameter defaults
);
so whenever I visit domain.com/test
This would invoke the test action in the controller called MarketingSite.
What I want to achieve is when I visit domain.com/load-from-db and since I don't have an action called load-from-db, I want to direct the request to a specific action and load-from-db becomes the parameter for that action. In that action, i'm going to read something from the database based on the parameter and then return a view. If I specify an action that exist, then it would just call that action.
Any help?
First note - the routing engine will apply the routes in the order you list them, checking each, and continuing if it cannot match. You currently have the "default" route setup as a catch-all - if is doesn't find a controller it uses "MarketingSite", if there is no action it uses "index". If I am understanding your problem, you don't want an "Index" action at all, you want to call another action, and pass the query to that.
You could try:
//Look for a matching action
routes.MapRoute(
"MatchAction", // Route name
"{action}/{id}",
new { controller = "MarketingSite", id = UrlParameter.Optional}
);
//With a single segment, pass that to a specific action as a parameter.
routes.MapRoute(
"load-from-db", // Route name
"{load-from-db}", // URL with parameters
new { controller = "MarketingSite", action = "MyAction"}
);
//With no segments (ex. domain.com/) use the Index on MarketingSite as default.
routes.MapRoute(
"Default", // Route name
"", // URL with parameters
new { controller = "MarketingSite", action = "Index"}
);
However, these routes might not accomplish exactly what you are looking for - getting your routes mapped in MVC can be a bit tricky. You should check this this out, and design your routes accordingly.
Hope that helps,
Chris
Override the HandleUnknownAction method on the MarketingSite controller:
protected override void HandleUnknownAction(string actionName) {
// TODO: check for actionName in database
}

Custom routes management

Is it possible to use custom routes handling code?
For example client requests server on http://server.com/api/v1/json/profile/ and my code calls ApiController, MyAction action with parameters version=1, format=json, action=profile.
Something like this? You'll have to use a different parameter name for action so you don't have a conflict with the controller action.
.MapRoute("name", "api/v{version}/{format}/{_action}", new { controller = "ApiController", action = "MyAction" });
EDIT made version work the way you wanted.
I would start off by renaming the "action" parameter to something else otherwise the route is going to get very confusing (maybe call it purpose?). Also, I believe something like the following would work:
routes.MapRoute(
// name of your route
"MyRoute",
// route template
"api/v{version}/{format}/{purpose}",
// default route values
new {
controller = "ApiController",
action = "MyAction",
},
// constraints placed on parameters (make sure they are valid)
new {
version = #"^\d+$", // number only (can also include decimals)
format = #"^(json|text|xml)$", // if you want filtering...
}
);
Then:
public ApiController : Controller
{
public ActionResult MyAction(Int32 version, String format, String purpose)
{
throw new NotImplementedException();
}
}

ASP.NET MVC Map String Url To A Route Value Object

I am creating a modular ASP.NET MVC application using areas. In short, I have created a greedy route that captures all routes beginning with {application}/{*catchAll}.
Here is the action:
// get /application/index
public ActionResult Index(string application, object catchAll)
{
// forward to partial request to return partial view
ViewData["partialRequest"] = new PartialRequest(catchAll);
// this gets called in the view page and uses a partial request class to return a partial view
}
Example:
The Url "/Application/Accounts/LogOn" will then cause the Index action to pass "/Accounts/LogOn" into the PartialRequest, but as a string value.
// partial request constructor
public PartialRequest(object routeValues)
{
RouteValueDictionary = new RouteValueDictionary(routeValues);
}
In this case, the route value dictionary will not return any values for the routeData, whereas if I specify a route in the Index Action:
ViewData["partialRequest"] = new PartialRequest(new { controller = "accounts", action = "logon" });
It works, and the routeData values contains a "controller" key and an "action" key; whereas before, the keys are empty, and therefore the rest of the class wont work.
So my question is, how can I convert the "/Accounts/LogOn" in the catchAll to "new { controller = "accounts", action = "logon" }"??
If this is not clear, I will explain more! :)
Matt
This is the "closest" I have got, but it obviously wont work for complex routes:
// split values into array
var routeParts = catchAll.ToString().Split(new char[] { '/' }, StringSplitOptions.RemoveEmptyEntries);
// feels like a hack
catchAll = new
{
controller = routeParts[0],
action = routeParts[1]
};
You need to know what part is what in the catchAll parameter. Then you need to parse it yourself (like you are doing in your example or use a regexp). There is no way for the framework to know what part is the controller name and what is the action name and so on, as you haven't specified that in your route.
Why do you want to do something like this? There is probably a better way.

Route Links - Url.Action

I'm trying to return my links so they display as /Area_1419.aspx/2/1.
I've managed to get that result in example 2 but I don't understand why it works, as I would exspect example 1 below to work.
I don't see how Example 2 knows to go to the Area_1419 controller?
Route
routes.MapRoute(
"Area_1419 Section",
"Area_1419.aspx/{section_ID}/{course_ID}",
new { controller = "Home", action = "Index" }
);
Links Example 1
<a href='<%=Url.Action("Area_1419",
new { section_ID="2", course_ID="1" })%>'><img .../></a>
Returns: /Home.aspx/Area_1419?section_ID=2&course_ID=1
Links Example 2
<a href='<%=Url.Action("index",
new { section_ID="2", course_ID="1" })%>'><img .../></a>
Returns: /Area_1419.aspx/2/1
Remember - URLs are detached from your controllers and their actions.
That means - even bizzare URL such as "trolololo/nomnomnom/1/2/3" might and might not call Home/Index or any other controller/action combo.
In your case - example 2 actually does not know how to go to Area_1419 controller.
Url.Action figures out url from these route details:
"Area_1419.aspx/{section_ID}/{course_ID}"
But link still will call Home controller Index action because of default route values:
new { controller = "Home", action = "Index" }
Assuming that you got Area_1419 controller with Index action, your route should look like:
routes.MapRoute(
"Area_1419 Section",
"Area_1419.aspx/{section_ID}/{course_ID}",
new { controller = "Area_1419", action = "Index" } //changes here
);
This is what you are calling.
UrlHelper.Action Method (String, Object)
Generates a fully qualified URL to an action method by using the specified action name and route values.
This method overload does not try to figure out appropriate controller. It assumes that you know it (takes it out from current route values) and understands first string argument as an action name.
Try to use this one.
UrlHelper.Action Method (String, String, Object)
Generates a fully qualified URL to an action method by using the specified action name, controller name, and route values.
In your case:
Url.Action("Index","Area_1419", new { section_ID="2", course_ID="1" });
You can use Url.RouteUrl(), in your case
Url.RouteUrl("Area_1419 Section", new { controller = "Home", action = "Index", section_ID="2", course_ID="1"}
to be sure you use the correct route name, and get the correct URL no-matter-what.

ASP.NET MVC view locations and routing

I have a base controller that I use to return basic views like this.
public ActionResult Index(string pageName)
{
return View(pageName);
}
public ActionResult LanguageSpecific(string ul, string pageName)
{
var result = View("sv/" + pageName);
return View(result.ViewName);
}
The controller's name is home is there a way that for it not to look for the sv content in /home but just in /sv
"EnglishRoute", // Route name
"{pageName}.aspx", // URL with parameters
new { controller = "Home", action = "Index", pageName = "" } // Parameter defaults
);
routes.MapRoute(
"SwedishRoute", // Route name
"{ul}/{pageName}.aspx", // URL with parameters
new { controller = "Home", action = "LanguageSpecific", ul = "",pageName = "" } // Parameter defaults
);
It looks in these locations:
~/Views/Home/sv/index.aspx
~/Views/Home/sv/index.ascx
When you call the View method you can pass in an app-relative path that starts with "~/" and then ASP.NET MVC will use the exact path you specify:
return View("~/UseExactlyThisFile.aspx");
That way it won't do its search in the various paths and locations that are pre-configured.
Please keep in mind that this doesn't have very much to do with routing (though it does a little bit).
If you try to localize your pages, why don't you use resources? With the pattern above you don't really take the advantages of mvc. Or do i misunderstand you? A simple solution would be to use an action filter which picks up the language identifier from the route and sets the UICulture. The Views then may use resources to localize their content.

Resources