Force route placeholder to be always treated as query string parameter - asp.net-mvc-routing

I have the following route on an action within an controller that has a route prefix:
[Route("{page?}", Name = "MyRoute")]
public async Task<ActionResult> Index(int page = 1)
IUrlHelper.RouteUrl gives me an url ending with /1 for MyRoute and Page = 1 as route values. This isn't wrong but I'd like page to always be a query string parameter.
How can I do this in MVC 6? If possible I want to avoid parsing the result of RouteUrl method and setting the page as query string parameter myself. Routing docs are still empty. This answer suggests using [FromUri]attribute although it sounds like this serves a different purpose and it seems not be available in MVC 6.

I got it to work by not having the parameter {page?} in the route.
Initially I had [Route("Bar/{page?}", Name = "BarRoute")] which was giving me /Bar/1
I got it to work with an Action set up like:
[Route("Bar", Name = "BarRoute")]
public IActionResult Foo(string page)
{
ViewBag.PN = page;
return View("Index");
}
This razor mark up:
<li>#Html.RouteLink("Click here!", "BarRoute", new { page = 1 })</li>
Generated:
<li>Click here!</li>
And in a Controller:
ViewBag.Thing = this.Url.RouteUrl("BarRoute", new { page = 1 });
Gave:
/Bar?page=1
Additionally, [Route("", Name = "BarRoute")] generated http://localhost:62405/?page=1

Related

MVC Routing: How to route /mypath/Default.aspx to /Default.aspx and keep QueryString?

We upgraded our solution to MVC 2. Outside links are still using /mypath/Default.aspx with a query string of n=10. Is there any way to catch that route with a controller and call a Default.aspx file with the proper query string?
We tried simply rerouting with IIS6 as well as a meta refresh, but both strip off the query string.
Nick Craver's answer looks promising as an answer to this question.
I'm not sure what you mean by "and call a Default.aspx file with the proper query string?" but if you mean to call your default route, then it can easily be done.
You should be able to just specify a route on "mypath/Default.aspx". The querystring will be automatically bound to your method.
For example:
routes.MapRoute(
"LegacyUrl", // Route name
"mypath/Default.aspx", // URL with parameters
new { controller = "Home", action = "Index"}
);
Then your method:
[HttpGet]
public ActionResult Index(int n)
{
// do something with n, maybe pass it to the View
return View();
}

Can my MVC2 app specify route constraints on Query String parameters?

My MVC2 app uses a component that makes subsequent AJAX calls back to the same action, which causes all kinds of unnecessary data access and processing on the server. The component vendor suggests I re-route those subsequent requests to a different action. The subsequent requests differ in that they have a particular query string, and I want to know whether I can put constraints on the query string in my route table.
For example, the initial request comes in with a URL like http://localhost/document/display/1. This can be handled by the default route. I want to write a custom route to handle URLs like http://localhost/document/display/1?vendorParam1=blah1&script=blah.js and http://localhost/document/display/1?vendorParam2=blah2&script=blah.js by detecting "vendor" in the URL.
I tried the following, but it throws a System.ArgumentException: The route URL cannot start with a '/' or '~' character and it cannot contain a '?' character.:
routes.MapRoute(
null,
"Document/Display/{id}?{args}",
new { controller = "OtherController", action = "OtherAction" },
new RouteValueDictionary { { "args", "vendor" } });
Can I write a route that takes the query string into account? If not, do you have any other ideas?
Update: Put simply, can I write routing constraints such that http://localhost/document/display/1 is routed to the DocumentController.Display action but http://localhost/document/display/1?vendorParam1=blah1&script=blah.js is routed to the VendorController.Display action? Eventually, I would like any URL whose query string contains "vendor" to be routed to the VendorController.Display action.
I understand the first URL can be handled by the default route, but what about the second? Is it possible to do this at all? After lots of trial and error on my part, it looks like the answer is "No".
QueryString parameters can be used in constraints, although it's not supported by default. Here you can find an article describing how to implement this in ASP.NET MVC 2.
As it is in Dutch, here's the implementation. Add an 'IRouteConstraint' class:
public class QueryStringConstraint : IRouteConstraint
{
private readonly Regex _regex;
public QueryStringConstraint(string regex)
{
_regex = new Regex(regex, RegexOptions.IgnoreCase);
}
public bool Match (HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
// check whether the paramname is in the QS collection
if(httpContext.Request.QueryString.AllKeys.Contains(parameterName))
{
// validate on the given regex
return _regex.Match(httpContext.Request.QueryString[parameterName]).Success;
}
// or return false
return false;
}
}
Now you can use this in your routes:
routes.MapRoute("object-contact",
"{aanbod}",
/* ... */,
new { pagina = new QueryStringConstraint("some|constraint") });
You don't need a route for this. It is already handled by the default model binder. Query string parameters will be automatically bound to action arguments:
public ActionResult Foo(string id, string script, string vendorname)
{
// the id parameter will be bound from the default route token
// script and vendorname parameters will be bound from the request string
...
}
UPDATE:
If you don't know the name of the query string parameters that will be passed you could loop through them:
foreach (string key in Request.QueryString.Keys)
{
string value = Request.QueryString[key];
}
This post is old, but couldn't you write a route before your default route
this would only catch routes with "vendor" in the args
routes.MapRoute(
null,
"Document/Display/{id}?{args}",
new { controller = "VendorController", action = "OtherAction" },
new {args=#".*(vendor).*"}//believe this is correct regex to catch "vendor" anywhere in the args
);
And This would catch the rest
routes.MapRoute(
null,
"Document/Display/{id}?{args}",
new { controller = "DisplayController", action = "OtherAction" }
);
Haven't tried this and I am a novice to MVC but I believe this should work?? From what I understand if the constraint doesn't match the route isn't used. So it would test the next route. Since your next route doesn't use any constraint on the args, it should, match the route.
I tried this out and it worked for me.

ASP.NET Mvc - nullable parameters and comma as separator

How should I define route in my global.asax to be able use nullable parameters and coma as separator?
I'm trying to implement routing rule for my search users page like
"{Controller}/{Action},{name},{page},{status}"
Full entry from the Global.asax:
routes.MapRoute(
"Search",
"{controller}/{action},{name},{page},{status}",
new { controller = "User", action = "Find",
name = UrlParameter.Optional,
page = UrlParameter.Optional,
status = UrlParameter.Optional }
);
Routine defined like above works fine when I'm entering all parameters, but when some parameters are equal to null routing fails (for example "user/find,,,")
According to Clicktricity comment bellow - the singature of action method that handes the request:
public ActionResult Find(string userName, int? page, int? status)
{
// [...] some actions to handle the request
}
On the beginning I was testing the route by VS debugger, now I'm using route debugger described on Phil's Haack blog. The tool confirm - that routing with null values is not properly handled (or I'm doing something wrong ;) )
As far as I know .Net routing doesn't let you do multiple nullable parameters like that. Multiple parameters will only work if they are missing working backwards from the end and with the separator also missing so you'd get matches on
user/find,bob,2,live
user/find,bob,2
user/find,bob
user/find
It'd be a lot easier to use querystrings for what you're trying to do.
Edit based on comment:
If this is necessary then you could try doing it this way (though it's not a nice approach)
Change your path to match
{Controller}/{Action},{*parameters}
Make sure to put a constraint on the action and controller so this is limited to as few as possible.
Rename each action that would take your full list to something else, adding a standard prefix to each one would be the cleanest way, and add the [NonAction] attribute. Add a new method with the original name that takes a string, this string is a comma separated string of your variables. In this method split the string and return the original action passing in the values from the split.
So you go from:
public ActionResult Find(string name, int page, string status){
//Do stuff here
return View(result);
}
To
public ActionResult Find(string parameters){
string name;
int? page;
string status;
//split parameters and parse into variables
return FindAction(name, page, status);
}
[NonAction]
public ActionResult FindAction(string parameters){
//Do what you did in your previous Find action
return View(results);
}

Return PartialView in MVC3 Area is not searching in area

I am working on an ASP.Net MVC 3 RC project. I have one area named Drivers. I have a LoadPartial() action in a controller in the Drivers area that returns a PartialView(string, object); When this is returned I get an error on my webpage that says "The partial view 'PublicAttendanceCode' was not found." It searched the following locations:
~/Views/AttendanceEvent/PublicAttendanceCode.aspx
~/Views/AttendanceEvent/PublicAttendanceCode.ascx
~/Views/Shared/PublicAttendanceCode.aspx
~/Views/Shared/PublicAttendanceCode.ascx
~/Views/AttendanceEvent/PublicAttendanceCode.cshtml
~/Views/AttendanceEvent/PublicAttendanceCode.vbhtml
~/Views/Shared/PublicAttendanceCode.cshtml
~/Views/Shared/PublicAttendanceCode.vbhtml
Why is it not searching in the Drivers Area?
I have the following pretty basic routes in Global.asax.cs:
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home",
action = "Index",
id = UrlParameter.Optional // Parameter defaults
}
);
}
And in DriversAreaRegistration.cs
public override void RegisterArea(AreaRegistrationContext context)
{
context.MapRoute(
"Drivers_default",
"Drivers/{controller}/{action}/{id}",
new { action = "RequestLeave", id = UrlParameter.Optional }
);
}
What am I missing that will make it look in the drivers area for the partial?
How are you providing area name to PartialView() method? I think you should be passing it in new { area = "Drivers" } as routeValues parameter.
The way that the MVC view engines know the area that they should look in is based on the route that was used to process the request.
In the case of the controller action that you have, are you certain that the request was processed by the area's route definition, or is it possible that the request was processed by the more general route that you defined in global.asax?
There are only four overloads of the method PartialView and it seems like neither of them accept routeValues as a parameter.
I solved this problem like this:
return PartialView(
VirtualPathUtility.ToAbsolute("~/Areas/MyArea/Views/Shared/MyView.cshtml"));
It works, but looks ugly.
This works too:
return PartialView("~/Areas/Admin/Views/Shared/MyView.cshtml", model);

ASP.NET MVC 2.0: How to read querystring value

I am trying to build a small ASP.NET MVC 2 application.I have a controller class with the below method in it
public ActionResult Index()
{
TestMvc.Models.PersonalInformation objPerson = new TestMvc.Models.PersonalInformation();
objPerson.FirstName = "Shyju";
objPerson.LastName = "K";
objPerson.EmailId="shyju#company.com";
return View(objPerson);
}
And when the page (View) being called, i can see this data there as my view has these data's displaying. Now i want to know how can i pass a query string in the url and use that id to build the PersonalInformation object.Hoe can i read the querystring value ? Where to read ?
I want the quesrtstring to be like
http://www.sitename/user/4234 where 4234 is the user id
http://www.sitename/user/4234 is not a querystring. The querystring is the part of the URL that comes after the ?, as in http://www.sitename/user?userId=42
However, the default routes that come with the MVC project template should allow you to simply change the signature of your action method to
public ActionResult Index(int id)
and you should get the desired result. You should look into how routing works in MVC if you want full control of your URLs.
Also, note that the index action is usually used for showing a list of all objects, so you probably want the Details action for showing 1 user object.
What you want is to modify your action to accept an id like so:
public ActionResult Index(string id)
{
TestMvc.Models.PersonalInformation objPerson = new TestMvc.Models.PersonalInformation();
if (!string.isNullOrEmpty(id))
{
objPerson = getPerson(id);
}
return View(objPerson);
}
Then add a route to your global.asax:
routes.MapRoute(
"MyRoute", // Route name
"user/{id}", // URL with parameters
new { controller = "mycontroller", action = "index", id = ""} // Parameter defaults
);