hypen in MVC 3 routes - asp.net-mvc-3

Here is my desired url format: /product-24-hid-35wh4-cx-dsgtx
How can I map this URL to my action method:
public ActionResult Product(int id)
Here is my routing code:
routes.MapRoute(
"ProductDetail",
"product-{id}-{name}",
new { controller = "product", action = "detail", name = UrlParameter.Optional },
new string[] { "SphereLight.Controllers" }
);
However, it does not work; I used phil haack's routedebugger to test this route, and below is the result:
Key Value
name dsgtx
id 24-hid-35wh4-cx
controller product
action detail
Only id = 24 is correct.
In one word, I need a route to match:
/product-24
/product-24-
/product-24-hid-35wh4-cx-dsgtx

Try to add constraints in your MapRoute:
routes.MapRoute(
"ProductDetail",
"product-{id}-{name}",
new { controller = "product", action = "detail", name = UrlParameter.Optional },
new { id = #"\d+" }, // <-- change it for #"[^-]+", if it can be non-digit
new string[] { "SphereLight.Controllers" }
);
UPDATE:
Finally got it.
The main problem is that you can't use parameters which contains the same separator.
For example, the example above will work with /product-24-nm, but not with product-24-nm-smth.
So, let's try this solution:
I've made it on the default routing, you can make it your way
routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = UrlParameter.Optional },
new MyRouteHandler()
).RouteHandler = new MyRouteHandler();
Implementation of MyRouteHandler:
public class MyRouteHandler : MvcRouteHandler
{
private static readonly Regex ProductPattern = new Regex(#"product\-(\d+)\-?(.*)");
protected override IHttpHandler GetHttpHandler(RequestContext requestContext)
{
var match = ProductPattern.Match(requestContext.RouteData.Values["controller"].ToString());
if (match.Length > 0)
{
requestContext.RouteData.Values["controller"] = "Home";
requestContext.RouteData.Values["action"] = "Detail";
requestContext.RouteData.Values["id"] = match.Groups[1].Value;
requestContext.RouteData.Values["name"] = match.Groups[2].Value;
}
return base.GetHttpHandler(requestContext);
}
}
So, the main idea is to check if the values matches our pattern product-id-name in the handler, and not trying to make it in MapRoute.
Hope this helps.

Related

Url.RouteUrl returning empty

I´m trying to get full URL but the RouteUrl is returning empty.
In the View, I´m calling like that:
alert('#Url.RouteUrl("Api", new { controller = "Parametros", id = "" })');
Here is my routes configurations:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapHttpRoute(
name: "Api",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Usuario",
action = "Login", id = UrlParameter.Optional }
);
}
and my controller:
public class ParametrosController : ApiController
{
ISistemaService _sistemaService;
public ParametrosController(Empresa empresa, ISistemaService sistemaService)
{
_sistemaService = sistemaService;
}
public PagedDataModel<ParametroDTO> Get(
[FromUri]ParametroFiltro filter, int page, int pageSize)
{
int total = 0;
var list = _sistemaService.Paging(filter, page, pageSize, out total);
return new PagedDataModel<ParametroDTO>(page, pageSize, total, list);
}
public ParametroDTO Get(string codigo)
{
return _sistemaService.GetParametroPorCodigo(codigo);
}
}
Add httproute = "" to the routeValues:
alert('#Url.RouteUrl("Api",
new { httproute = "", controller = "Parametros", id = "" })');

Unit Test ASP MVC Route with Constraint

I have a route that is defined like this:
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional, area = "" }, // Parameter defaults
new { home = new HomePageConstraint() }
);
public class HomePageConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return !httpContext.Request.RawUrl.StartsWith("/home", StringComparison.InvariantCultureIgnoreCase);
}
}
And I am trying to test it like this:
[Test]
public void Home_Load_Homepage()
{
"~/".ShouldMapTo<HomeController>(x => x.Index());
}
The problem I have is that the httpContext is null, so the test fails. how can I inject http context into a constraint?
In the end I did this:
var context = new FakeHttpContext("~/");
var fakeRequest = new FakeRequest("~/", new Uri("http://localhost/"), new Uri("http://localhost/"));
context.SetRequest(fakeRequest);
var route = RouteTable.Routes.GetRouteData(context);
route.ShouldMapTo<HomeController>(x => x.Index());

ASP.NET MVC - How to register custom route?

I've created a custom Route and registered it in Global.asax like this:
routes.Add(
null,
new SeoRoute(
"foo/{id}/{title}",
new { controller = "Foo", action = "Details" }
));
Since I'm using Areas in my application, I have to set Namespaces for each Route.
With regular routes, I do it like this:
routes.MapRoute(
null,
"foo",
new { controller = "Foo", action = "Index" },
new string[] { "Boo.Web.Controllers" }
);
But how can I set namespaces for custom routes?
Any help would be greatly appreciated!
I used ILSpy too see how MapRoute works.
I didn't set DataTokens value. Here's the solution:
Route class:
public class SeoRoute : Route
{
public SeoRoute(string url, object defaultValues, string[] namespaces)
: base(url, new RouteValueDictionary(defaultValues), new MvcRouteHandler())
{
if(namespaces != null && namespaces.Length > 0)
{
DataTokens = new RouteValueDictionary();
DataTokens["Namespaces"] = namespaces;
}
}
...
}
Global.asax:
routes.Add(
null,
new SeoRoute(
"foo/{id}/{title}",
new { controller = "Foo", action = "Details" },
new string[] { "Boo.Web.Controllers" }
));

mvc3 Routes setup as id, id2 id3

I have the following area routes setup.
context.MapRoute(
"Admin_default3",
"Admin/{controller}/{action}/{id}/{id2}/{id3}",
new { action = "Index" }
);
context.MapRoute(
"Admin_default2",
"Admin/{controller}/{action}/{id}/{id2}",
new { action = "Index"}
);
context.MapRoute(
"Admin_default",
"Admin/{controller}/{action}/{id}",
new { action = "Index", id = UrlParameter.Optional }
);
When a controller action is hit I do something like the following where I place the params into readable variable names.
public ActionResult Search(Guid? id, int? id2, bool? id3)
{
Guid? source = id;
int daysOld = id2;
bool includeNonEnglish = id3;
//.... Action!
}
Should I continue that way? Should I create a plethora of routes?
thank you
I would create more routes. That way, you have things like:
Html.ActionLink(title, "Action", "Controller", new { source = <value>, daysOld = <value>, includeNonEnglish = <value> });
Instead of:
Html.ActionLink(title, "Action", "Controller", new { id = <value>, id2 = <value>, id3 = <value> });
Among other things (like AJAX calls with jQuery, where you use Json for specifying parameters). It would make things more readable. It would also help if you're using, or going to use, T4MVC.

MapRouting default querystring values?

I have this route map (notice that topicName is ignored):
routes.MapRoute(
"Topics", // Route name
"Topic/{topicName}/{action}",
new { controller = "Topic", action = "AddQuestion" });
And I want it to defaultly map to this Url:
http://localhost:51421/Topic/SomeName/AddQuestion?topicId=1 (or if that's not possible,to this url: http://localhost:51421/Topic/SomeName/AddQuestion/topicId/1)
(which should invoke this action:
public ActionResult AddQuestion(int topicId)
{
return View();
}
)
But either way I need all this data in the url.
What's the correct way to do that?
You could add a default route:
routes.MapRoute(
"Topics",
"Topic/{topicName}/{action}/{topicId}",
new { controller = "Topic", action = "AddQuestion", topicId = "1" }
);
routes.MapRoute(
"Default",
"{controller}/{action}/{topicId}",
new { controller = "Topic", action = "AddQuestion", topicId = "1" }
);

Resources