Why isn't string parameter being routed to an action? - c#

In my MVC5 application I am trying to pass a string to an action.
In PodcastsController I have an action called Tagged:
public ActionResult Tagged(string tag)
{
return View();
}
For example, if I wanted pass the string Testto the Tagged action, it would look like this in the url:
/Podcasts/Tagged/Test
I have a route set up like this:
routes.MapRoute(
"Podcasts",
"Podcasts/Tagged/{tag}",
new { controller = "Podcasts", action = "Tagged", tag = UrlParameter.Optional }
);
Edit I am calling the Tagged action like this:
if (!string.IsNullOrEmpty(tagSlug))
{
return RedirectToAction("Tagged", "Podcasts", new { tag = tagSlug });
}
When I set a break point on the Taggedaction, tag is always null
Can anyone see what I am doing wrong?
I'm pretty sure there's something wrong with the route but I can't figure out what...

The reason you are getting a null is because you are not actually passing it a parameter called tag in your code:
if (!string.IsNullOrEmpty(tagSlug))
{
return RedirectToAction("Tagged", "Podcasts", new { tagSlug });
}
When you omit the property name it takes the variable name, so you are actually passing a variable tagSlug which your action does not accept. Try this instead:
if (!string.IsNullOrEmpty(tagSlug))
{
return RedirectToAction("Tagged", "Podcasts", new { tag = tagSlug });
}

It must be working exacly like this....
Then go to http://localhost:64147/podcast/tagged/hi and recive hi as a value of tag
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "MyRoute",
url: "podcast/tagged/{tag}",
defaults: new { controller = "Podcasts", action = "Tagged", tag = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}

I'm sorry i'm asking here, but did you check that your route precedes any other routes that could overlay?
The routing is done by taking them in order. The first route that corresponds to the given parameters is chosen so if you have this route first
{controller}/{action}/{id}
and then
Podcasts/Tagged/{tag}
you will get null because the first route is chosen
Route order actually matters

Related

Since I wrote a route for a specific Action of a Controller, do I need to write a route for all Actions inside the Controller?

I'm writing few routes for my MVC application. I have the following routes for my application:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Pages", action = "Index", id = UrlParameter.Optional }
);
The route above is used when I want to access default values like:
www.servicili.com/budget/edit/1
www.servicili.com/professional/view/234
But, I create the following route for a specific purpose:
routes.MapRoute(
name: "Perfil",
url: "{UsuApelido}",
defaults: new { controller = "Perfil", action = "Index"}
);
the route above, is used to access the URL profile of a "plumber" for example:
www.servicili.com/MarkZuckberg
the profile details are on the controller Perfil and Action Index, however, since I wrote this route, all other actions aren't working.
For example: If I try to access the Index action inside another controller, it redirect to Index of Perfil.
--
The question is: Since I wrote a route for a specific Action of a Controller, do I need to write a route for all Actions inside the Controller?
To solve your problem try like this,
First define constraint,
public class PlumberUrlConstraint: IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
var db = new YourDbContext();
if (values[parameterName] != null)
{
var UsuApelido = values[parameterName].ToString();
return db.Plumbers.Any(p => p.Name == UsuApelido);
}
return false;
}
}
Define two routes, put "Default" route at 2nd position
routes.MapRoute(
name: "Perfil",
url: "{*UsuApelido}",
defaults: new { controller = "Perfil", action = "Index"},
constraints: new { UsuApelido = new PlumberUrlConstraint() }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Pages", action = "Index", id = UrlParameter.Optional }
);
Now if you have an 'Index' action in 'Perfil' Controller, you can get plumber name like this,
public ActionResult Index(string UsuApelido)
{
//load the content from db with UsuApelido
//display the content with view
}
Hope this help.

Custom URL Routing in Asp.Net MVC 4

How can i do like this url (http://www.domain.com/friendly-content-title) in Asp.Net MVC 4.
Note: This parameter is always dynamic. URL may be different: "friendly-content-title"
I try to Custom Attribute but I dont catch this (friendly-content-title) parameters in ActionResult.
Views:
Home/Index
Home/Video
ActionResult:
// GET: /Home/
public ActionResult Index()
{
return View(Latest);
}
// GET: /Home/Video
public ActionResult Video(string permalink)
{
var title = permalink;
return View();
}
RouteConfig:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Home Page",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Video Page",
url: "{Home}/{permalink}",
defaults: new { controller = "Home", action = "Video", permalink = "" }
);
}
What should I do for catch to url (/friendly-content-title)?
To enable attribute routing, call MapMvcAttributeRoutes during configuration. Following are the code snipped.
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
}
}
In MVC5, we can combine attribute routing with convention-based routing. Following are the code snipped.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapMvcAttributeRoutes();
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
It is very easy to make a URI parameter optional by adding a question mark to the route parameter. We can also specify a default value by using the form parameter=value. here is the full article.
Radim Köhler's solution is a good one.
But another option if you want more control over routing is using a custom constraint.
Here's an example
RouteConfig.cs
routes.MapRoute(
"PermaLinkRoute", //name of route
"{*customRoute}", //url - this pretty much catches everything
new {controller = "Home", action = "PermaLink", customRoute = UrlParameter.Optional},
new {customRoute = new PermaLinkRouteConstraint()});
So then on your home controller you could have action like this
HomeController.cs
public ActionResult PermaLink(string customRoute)
{
//customRoute would be /friendly-content-title..do what you want with it
}
The magic of this happens in the IRouteConstraint that we specified as the 4th argument in the MapRoute call.
PermaLinkRouteConstraint.cs
public class PermaLinkRouteConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
var permaRoute = values[parameterName] as string;
if (permaRoute == null)
return false;
if (permaRoute == "friendly-content-title")
return true; //this indicates we should handle this route with our action specified
return false; //false means nope, this isn't a route we should handle
}
}
I just wanted to show a solution like this to show you can basically do anything you want.
Obviously this would need to be tweaked. Also you'd have to be careful not to have database calls or anything slow inside the Match method, as we set that to be called for every single request that comes through to your website (you could move it around to be called in different orders).
I would go with Radim Köhler's solution if it works for you.
What we would need, is some marker keyword. To clearly say that the url should be treated as the dynamic one, with friendly-content-title. I would suggest to use the keyword video, and then this would be the mapping of routes:
routes.MapRoute(
name: "VideoPage",
url: "video/{permalink}",
defaults: new { controller = "Home", action = "Video", permalink = "" }
);
routes.MapRoute(
name: "HomePage",
url: "{controller}/{action}",
defaults: new { controller = "Home", action = "Index" }
);
Now, because VideoPage is declared as the first, all the urls (like these below) will be treated as the dynamic:
// these will be processed by Video action of the Home controller
domain/video/friendly-content-title
domain/video/friendly-content-title2
while any other (controllerName/ActionName) will be processed standard way

Asp.net mvc action having dynamic in parameter

Masters,
I've defined few routes as follow.
routes.MapRoute(
name: "Default1",
url: "{a}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default2",
url: "{a}/{b}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default3",
url: "{a}/{b}/{c}",
defaults: new { controller = "Home", action = "Index" }
);
routes.MapRoute(
name: "Default4",
url: "{a}/{b}/{c}/{d}",
defaults: new { controller = "Home", action = "Index" }
);
and in HomeController,
public ActionResult Index(dynamic data)
{
return View();
}
I set a break-point at begining of Index method
Now, when i hit URL like : http://{MylocalIP:port}/a/b sticks on break point.
but I am unable to extract route values that is a & b.
How can we do this?
Please help.
Thanks in advance
Even if you manage to get this to work you would have to case your controller action to handle the different parameter. Why not just create different actions depending on the number of parameters and avoid such usage altogether. If you are trying to provide properties of a Model that may not always have values then create a Model and instead of passing dynamic pass the Model to the action.
The modelbinder doesn't know what to do with a dynamic action parameter. The closest that I'm aware of is JObject, in JSON.net.
You will still have to figure out what what type you have received, deserialize it, and return the appropriate view.
I had a similar requirement of an action and a dynamic number of parameters. In my case, I needed to include a folder path as part of the URL. This path can include different number of sub-folders. MVC would interpret the sub-folders as parameters. I found a way to solve this in this article by Bipin Joshi.
I wrote my route this way:
routes.MapRoute(
name: "Portfolio",
url: "Portfolio/{*id}",
defaults: new { controller = "Portfolio", action = "Index", id = UrlParameter.Optional },
constraints: new { httpMethod = new HttpMethodConstraint("GET") }
);
I used a hard coded "Portfolio" because this route only affects that controller. You can make your route dynamic with :
url: "{controller}/{*id}"
I built the controller this way:
public class PortfolioController : Controller
{
public ActionResult Index(string id)
{
//Get Pictures from folder 'id'
}
}
You can check the results here.

MVC4 Dynamic Controller Path?

I have a SiteController class in my MVC4 project,the 'Site' url is working fine, but I need a dynamic url part right after 'Site', I need the url to look like this:
mysite.com/Site/{DYNAMICSTRING}/Users(or whatever)
{DYNAMICSTRING} can be the name of a subsite, so the controller should check if that subsite does actually exist in the database.
Right now I'm using Query strings, but that is not what my client wants.
How can I do that?
Additional details
My routing:
routes.MapRoute(
"Subdomain", // Route name
"{controller}/{action}/{dynamicString}", // URL with parameters
new { controller = "Site", action = "Subdomain" } // Parameter defaults
);
My controller:
public ActionResult Subdomain(string dynamicString)
{
return View();
}
the value of dynamicString is null when I navigate to: /Site/Subdomain/SomeString
You have to configure routing. For example if you have Homecontroller:
public class HomeController:Controller
{
public ActionResult Subdomain(string dynamicString)
{
return View();
}
}
then you have to configure your routing something like that
routes.MapRoute(
"Subdomain", // Route name
"{controller}/{action}/{dynamicString}/anyOtherParams", // URL with parameters
new { controller = "Home", action = "Subdomain", dynamicString = "" } // Parameter defaults
);
You can do it like this:
routes.MapRoute(
name: "Default", // Route name
url:"Site/{dynamicstring}", // URL with parameters
defaults: new {controller = "Site", action = "Index" } // Defaults
);
you can keep adding parts to the url part like so
url:"Site/{dynamicstring}/{anythingelse}" // url:"Site/{dynamicstring}/{anythingelse}/{bla}/Test/{hello}/etc..."
or you can also have a catch all route like this:"
routes.MapRoute(
name: "Default", // Route name
url:"{*all}", // catch all
defaults: new {controller = "Site", action = "Index" } // Defaults
);
and fetch all other parts in your controllers index action, by splitting them on /
Make sure you put the custom route before your default otherwise the default route will pick it up.
in your controller you get something like this:
public ActionResult Index(string dynamicstring, string anythingelse)
{
return View();
}
and if you then pass in a url like this:
http://www.mysite.com/Site/test.nl/jordy
your dynamicstring will have the value "test.nl" and your anythingelse will have "jordy"
I hope this helps

URL Parameter Not Recognized in Controller

I'm learning MVC 4, and it's my understanding that going to this URL should pass an int of 44 to the Edit() method of the controller. Indeed, when I go here:
http://localhost:51921/TrackerJob/Edit/44
... this method gets invoked:
public ActionResult Edit(int trackerJobId = -1)
{
Debug.WriteLine(trackerJobId);
}
... but the parameter is always -1. I had this working in a different project, but for some reason it's always -1 in this project. I don't see a difference between the two projects that would cause one to work and this one to fail. If I change the method signature to this:
public ActionResult Edit(int trackerJobId)
{
Debug.WriteLine(trackerJobId);
}
I get an error:
The parameters dictionary contains a null entry for parameter 'trackerJobId' of non-nullable type 'System.Int32'
Any ideas? I'm not sure what to check...
Edit - Including routes, by request*
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
If you want to use the default routing then just make sure your parameter is called id.
Otherwise you could add a new route like this:
routes.MapRoute(
name: "TrackerJob",
url: "{controller}/{action}/{jobtrackerid}",
defaults: new { controller = "TrackerJob", action = "Index", id = UrlParameter.Optional }
);
Make sure you add this route BEFORE the default route. The order of routes is very important!
Only you know if the trackerJobId is optional or not.
Note that if you want something more fancy you can tweak the routes to produce what you want.
e.g. If you want URLs like http://localhost:51921/TJ-E-44 for editing then your route would look like this:
routes.MapRoute(
name: "TrackerJobEdit",
url: "TJ-E-{jobtrackerid}",
defaults: new { controller = "TrackerJob", action = "Edit", id = UrlParameter.Optional }
);
I'm sure you get the idea.

Categories