MVC2 why is only my first route being recognized? - asp.net-mvc-2

So right now I have two routes:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
routes.MapRoute(
"Legos",
"{controller}/{action}/{page}",
new { controller = "Legos", action = "Lego", page = UrlParameter.Optional });
If I go to www.website.com, I will be able to see my homepage just fine, but my Legos route doesnt work. Like-wise, if I put my Legos route on top, I can go to www.website.com/Legos/Lego/1 just fine, but going to www.website.com thows an error(below), and I need to manually go to www.website.com/Home/Index to see my homepage.
Error:
The parameters dictionary contains a null entry for parameter 'page' of non-nullable type 'System.Int32' for method 'System.Web.Mvc.ActionResult Errors(Int32, System.String)' in 'stuff.Controllers.Legos'. An optional parameter must be a reference type, a nullable type, or be declared as an optional parameter.
Parameter name: parameters
Why would this be?

The reason is that both routes have the same number of parameters and no constraints, so the route handler cannot differentiate between them for an inbound request - it will just go with the first match (i.e. whichever has been added to the route table first).
If you correctly differentiate between your routes then it will work. For example, you could try removing the "id" optional parameter from the "Default" route and making the "page" part of the "Legos" route mandatory (as it appears that it is required for the endpoint to work, this is best practice anyway):
routes.MapRoute(
"Legos",
"{controller}/{action}/{page}",
new { controller = "Legos", action = "Lego", page = 1 });
routes.MapRoute(
"Default", // Route name
"{controller}/{action}", // URL with parameters
new { controller = "Home", action = "Index" } // Parameter defaults
);
This would mean that a url with two parameters (i.e. Home/Index) would not match the "Legos" route, as it is missing the required "page" parameter - it would then test the "Default" route, which would succeed as "Home/Index" matches the "{anything}/{anything}" format it expects.
Your other option would be to add a route constraint to one of the routes - for example you could make "page" only accept numbers, so if the incoming request looked like {anything}/{anything}/{numeric} it would match the "Legos" route, but if it was non-numeric it wouldn't match the constraint and would instead hit the default route:
routes.MapRoute(
"Legos",
url: "{controller}/{action}/{page}",
defaults: new { controller = "Legos", action = "Lego" },
constraints: new { page = #"([0-9]+)" } ); // constrain to "one or more numbers" - constraints are simply regular expressions
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional } // Parameter defaults
);
Of course the "problem" with this particular solution would be if you had a numeric id (which lets face it, most are) that you wanted to use for the "default" route, it would end up being caught by the "Legos" route...

Related

Why is this IgnoreRoute call matching these requests?

I've got a localization httphandler that's running in the context of my ASP.Net MVC2 Content folder (part of what it's doing is compiling .less files that are in /Content/css). My default route for this particular set of requests looks like this:
context.Routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
context.MapRoute(
"Area_default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new { controller = new VirtualDirectoryConstraint("VDirectory1") },
new string[] { "Namespace.One.MVC" }
);
(As an aside - I don't think it's relevant, but just in case - the VirtualDirectoryConstraint rejects matches on this route if the request is not coming from the passed-in application path/virtual directory)
With this configuration a call to http://server.net/VDirectory1/Content/css/LessCompiler.axd fails because there's no ContentController class. All well and good.
When I add
context.Routes.IgnoreRoute("{Content}/{*pathInfo}");
that call succeeds, but subsequent calls to
http://server.net/VDirectory1/Localization/ClientScript/en-US.js
and
http://server.net/VDirectory1/Authorization/ClientScript
fail. Looking at Phil Haack's RouteDebugger tool, those calls are matching the Content IgnoreRoute route:
True {Content}/{*pathInfo} (null) (null) (null)
and are therefore not being routed to the LocalizationController and AuthorizationController, respectively.
Clearly I'm misunderstanding something about how the IgnoreRoute is supposed to be used and why that particular IgnoreRoute is matching those requests. What am I missing?
Shouldn't your IgnoreRoute use Content instead of {Content} ?
context.Routes.IgnoreRoute("Content/{*pathInfo}");
At the moment, {Content} is probably being expanded as a variable to nothing, which makes the pathinfo match everything.

UriPathExtensionMapping in MVC 4

How do I use UriPathExtensionMapping in MVC4? I've added the mapping in the formatter such that:
MediaTypeMappings.Add(new UriPathExtensionMapping("json", new MediaTypeHeaderValue("application/json"))
But I can't use the extension on my route unless I add a verb, such as:
routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}.{extension}"
);
Then it'll recognize my desired media type, but it also starts expecting "extension" as a parameter on the action. Example:
public object Get(string extension)
{
}
Instead of just:
public object Get()
{
}
How can I resolve this?
Thanks!
I don't remember if I noticed this problem, but it might be because I explicitly specified the default value for the extension like:
var rF = routes.MapHttpRoute(
name: "DefaultApi.v1.format",
routeTemplate: "api/1/{controller}/{action}.{format}/{*id}",
defaults: new { id = RouteParameter.Optional, format = "json", version = 1 }
);
*note format = "json" in the line beginning with defaults.
I did also notice that this sometimes doesn't work on requests with trailing parameters for id, like ~/api/1/values/get.json/4.
I can't repro this problem.
You are right that, to use UriPathExtensionMapping, you will need to specify the {controller}.{ext} in your route, like what you described above. If you only specify the {controller}, and uri looks like ~/home.json, then the controller token will be mapped to home.json, which is probably not what you wanted.
However, you should not need to require an "extension" parameter in your action. If you continue seeing this problem, can you post your entire repro, including your controller, your routes and configuration set up with custom formatter? thank you.

Zend url : get parameter always stay in the url

I have some trouble using the Zend url helper with get parameter.
In a view, I have pagination which send extra parameters in get (so in the url), so that's ok. But that is not ok is that the parameters always stay in the url even if I change page.
In fact, the zend url helper - I use to generate the url of link or form's action - add automaticaly the parameter at the end of the url so whatever the link I click, I have this parameters...
//In my controller
$this->_view->url(array("action"=>"action-name");
// generate for example : "mywebsite/controller-name/action-name/pays/4" but I don't want the "/pays/4"
Thank you for your help
The url method accepts additional parameters. One of them resets the get-string parameters.
url (
array $ urlOptions = array(),
$ name = null,
$ reset = false,
$ encode = true
)
Generates an url given the name of a route.
Parameters:
array $urlOptions - Options passed to the assemble method of the Route object.
mixed $name - The name of a Route to use. If null it will use the current Route
bool $reset - Whether or not to reset the route defaults with those provided
Returns:
string Url for the link href attribute.
It's all in the doc. The above is for ZF version 1.10
The definition or url() is
public function url(array $urlOptions = array(), $name = null, $reset = false, $encode = true)
So try setting the third parameter ($reset) to true

Remove Index from MVC url with routeValue

How can I remove the Index from the MVC url that has a routeValue?
E.g.
http://localhost/Beverage/Index/WhiteWine
to
http://localhost/Beverage/WhiteWine
but still be able to have
http://localhost/Beverage/ShowBeverage/1
You can create a custom route:
MapRoute("My Route Name",
"Beverage/{id}",
new { controller = "Beverage", action = "Index" });
Note that the controller name must be hard-coded in the route, then specified in the defaults to tell MVC which controller to use.
If you take the naive approach and map {controller}/{id}, it will accept any URL of the form a/b, which is not what you want.

MapRoute with generic controller

Is it possible to map a route with MapRoute and specify a generic controller e.g
context.MapRoute(
"Dashboard_Edit", // Route name
"dashboard/edit/{*pagePath}",
new { controller = "Dashboard`1", action = "edit", pagePath = "home" }
);
It is unfortunately not allowed with the default controller factory. The type "Dashboard`1" is for an open generic type and cannot be constructed. In other words, with the default controller factory the only allowed values for "controller" are ones that can fit the following pseudo syntax:
IController c = new SomeControllerType();
The SomeControllerType must be valid (though without the "Controller" suffix or namespace), and it must have a parameterless constructor.
You could always write a custom controller factory that has more advanced functionality and understands how to construct generic types.