need help canonicalizing an HTTP GET form in asp.net mvc - asp.net-mvc

I have a form in an asp.net mvc site that serves 3 purposes: paging, sorting, and searching. These items should all be rendered in the same form, since returning the correct search results depends on variables from all 3 aspects. What I'm trying to do is move the parameters out of the querystring and put them in a canonical URL.
I'm almost there, here are my 3 route configurations so far (using T4MVC for area, controller, and action names):
context.MapRoute(null,
"my-area/my-widgets/search/{size}-results-max/page-{page}/order-by-{sort}",
new
{
area = MVC.MyArea.Name,
controller = MVC.MyArea.MyWidgets.Name,
action = MVC.MyArea.MyWidgets.ActionNames.Search,
page = UrlParameter.Optional,
size = UrlParameter.Optional,
sort = UrlParameter.Optional,
}
);
context.MapRoute(null,
"my-area/my-widgets/canonicalize-search",
new
{
area = MVC.MyArea.Name,
controller = MVC.MyArea.MyWidgets.Name,
action = MVC.MyArea.MyWidgets.ActionNames.CanonicalizeSearch,
}
);
context.MapRoute(null,
"my-area/my-widgets",
new
{
area = MVC.MyArea.Name,
controller = MVC.MyArea.MyWidgets.Name,
action = MVC.MyArea.MyWidgets.ActionNames.CanonicalizeSearch,
}
);
The form in the view submits to the CanonicalizeSearch route, using this syntax:
#using (Html.BeginForm(MVC.MyArea.MyWidgets.CanonicalizeSearch(),
FormMethod.Get))
In the MyWidgetsController, there are 2 action methods:
[ActionName("canonicalize-search")]
public virtual RedirectToRouteResult CanonicalizeSearch(string keyword,
int page = 1, int size = 10, string sort = "Title-Ascending")
{
var result = RedirectToRoutePermanent(new
{
area = MVC.MyArea.Name,
controller = MVC.MyArea.MyWidgets.Name,
action = MVC.MyArea.MyWidgets.ActionNames.Search,
page = page,
size = size,
sort = sort,
keyword = keyword,
});
return result;
}
[ActionName("search")]
public virtual ViewResult Search(string keyword,
int page = 1, int size = 10, string sort = "Title-Ascending")
{
// code to perform query
return View(model);
}
This works for moving all querystring variables into a canonicalized route except for the keyword. If I add a keyword parameter to the first route, the CanonicalizeSearch action only redirects to the Search action when keyword is not null, empty, or whitespace. This is no good as it makes browsing page results impossible when there is no keyword entered.
I think I've tried everything -- giving the keyword a default value in the controller, adding a 4th route that adds keyword to the other 3 parameters, etc. However the only way I can seem get this to work is by keeping keyword as a querystring parameter. (Actually I can get it to work by prepending an underscore to the keyword in CanonicalizeSearch and stripping it off in Search, but that's pretty hacky).
Any help?

Did you try setting UrlParameter.Optional on the keyword parameter in your first route? Sounds obvious and dumb, but you never ruled it out.

I think I stumbled on a better solution to this by trying to solve another problem.
Say someone types in "my search terms" in the keyword box. Submitting that causes the CanonicalizeSearch method to route to the path:
/my-area/my-widgets/search/10-results-per-page/page-1/
order-by-Title-Ascending/my%20search%20terms
Those %20 symbols are annoying. I would rather the URL look like this:
/my-area/my-widgets/search/10-results-per-page/page-1/
order-by-Title-Ascending/my-search-terms
I can accomplish this with the following (note the change from a permanent to a temporary redirect):
[ActionName("canonicalize-search")]
public virtual RedirectToRouteResult CanonicalizeSearch(string keyword,
int page = 1, int size = 10, string sort = "Title-Ascending")
{
var result = RedirectToRoute(new
{
area = MVC.MyArea.Name,
controller = MVC.MyArea.MyWidgets.Name,
action = MVC.MyArea.MyWidgets.ActionNames.Search,
page = page,
size = size,
sort = sort,
keyword = (string.IsNullOrWhiteSpace(keyword))
? "no-keywords" : keyword.Replace(' ', '-'),
});
TempData["keyword"] = keyword;
return result;
}
[ActionName("search")]
public virtual ViewResult Search(string keyword,
int page = 1, int size = 10, string sort = "Title-Ascending")
{
keyword = TempData["keyword"] as string ?? keyword;
// code to perform query
return View(model);
}
This solves both the question I posted here and the removal of the %20 symbols. Whenever the keyword is null empty or whitespace, it will render the URL
/my-area/my-widgets/search/10-results-per-page/page-1/
order-by-Title-Ascending/no-keywords
... and the route will always match.

Related

sending string from view to controller in asp.net

I'm trying to get data from view in string
Here's the code in View
And in controller, I'm getting this customer name like this:
public ActionResult ViewCustomerDetails(string c_name) {
List<Sale> customerList = new List<Sale>();
customerList = db.Sales.Where(x => x.sale_customer == c_name).ToList();
double total_cash_recieved = 0;
double total_amount = 0;
foreach (var customer in customerList) {
total_cash_recieved = total_cash_recieved + (double)customer.cash_recieved;
total_amount = total_amount = (double)customer.sale_amount;
}
double remaining_balance = total_amount - total_cash_recieved;
ViewBag.TotalAmount = total_amount;
ViewBag.TotalRecieved = total_cash_recieved;
ViewBag.TotalRemaining = remaining_balance;
return View(customerList);
}
But the problem is, in c_name variable, I'm getting null.
Anyone know how to correct it or solution?
Since your parameter name is c_name, you should include that in your querystring as Burak mentioned in his answer.
If you prefer, you can render the link using Html.ActionLink helper method.
Html.ActionLink("View","ViewCustomerDetails","Admin",new { c_name=customer.name},null)
Or if you prefer to keep the existing url you have, you can update your ViewCustomerDetails method's parameter name to Id so that with the default route definition, your unnamed parameter value will be mapped to Id parameter.
public ActionResult ViewCustomerDetails(string id) {
var c_name=id;
// your existing code
}
It is always a good idea to pass a unique Id ( Customer ID etc..) instead of passing a name to show the details because I know more than one scott in the world.
You should send it like this:
And make sure that #customer.name is not null before going to the server side.
or you can set new route to RouteConfig.cs
routes.MapRoute(
name: "Default2",
url: "Admin/ViewCustomerDetails/{c_name}",
defaults: new { controller = "Admin", action = "ViewCustomerDetails", c_name= UrlParameter.Optional }
);
You didn't pass the parameter to the controller.
You can always simply pass parameters as part of query string as long as action method on controller is expecting them by exactly the the same name in the signature.

Redirect To Action method not mapping correctly

I'm calling RedirectToAction but it isn't working properly.
I want the resulting URL to look like this:
https://localhost:44301/ManageSpaces/123/overview
but it looks like this and is missing the action portion of the URL:
https://localhost:44301/ManageSpaces/123
Here is my RedirectToAction call.
return RedirectToAction("overview", new RouteValueDictionary(
new {controller = "ManageSpaces", action = "overview", id = 123}));
Here is what my route looks like in RouteConfig:
routes.MapRoute("ManageSpaces",
"ManageSpaces/{id}/{action}",
new { controller = "ManageSpaces", action = "overview"},
new { id = #"\d+" } //The regular expression \d+ matches one or more integers
);
Maybe it is taking the default route. Rename, remove, or comment out the default route to see if that has any effect.
You have made your action route value optional by providing a default value. Optional values are ignored when resolving the URL.
routes.MapRoute("ManageSpaces",
"ManageSpaces/{id}/{action}",
new { controller = "ManageSpaces", action = "overview"},
new { id = #"\d+" } //The regular expression \d+ matches one or more integers
);
If you want to include the action in the URL, you have to make it a required argument.
routes.MapRoute("ManageSpaces",
"ManageSpaces/{id}/{action}",
new { controller = "ManageSpaces"},
new { id = #"\d+" } //The regular expression \d+ matches one or more integers
);

Routing with parameter conflicts issue

MY route config :
routes.MapRoute(
"LastTwoRoute",
"thong-ke-ket-qua-xo-so-2-so-cuoi/{cityID}/{pnumbers}/{pdays}/{ponlySpecial}",
new { controller = "LastTwo", action = "Index",
cityID = "MB",
pnumbers = "",
pdays = 1000,
ponlySpecial = false
});
The controller :
[HttpGet]
public ActionResult Index(string cityID, string pnumbers, int pdays, bool ponlySpecial)
{
[HttpGet]
public ActionResult Index(string cityID, string pnumbers, int pdays, bool ponlySpecial)
{
LastTwoParameters lastTwoParameters = new LastTwoParameters();
lastTwoParameters.listCities = Common.GetDropDownCitiesList();
lastTwoParameters.Numbers = pnumbers;
lastTwoParameters.Days = pdays;
lastTwoParameters.OnlySpecial = ponlySpecial;
lastTwoParameters.listLastTwoResult = new List<getReport_LastTwo_Result>();
if (TempData["Redirection"] != null || !string.IsNullOrEmpty(pnumbers) )
{
if (!string.IsNullOrEmpty(cityID) && pdays > 0)
{
using (KQXS context = new KQXS())
{
lastTwoParameters.listLastTwoResult = context.getReport_LastTwo(cityID, pnumbers, pdays, ponlySpecial).ToList();
}
}
}
return View(lastTwoParameters);
}
[HttpPost]//Run action method on form submission
public ActionResult Index(List<Cities> c, string cityID, string numbers, int days, bool onlySpecial)
{
TempData["Redirection"] = true;
return RedirectToRoute("LastTwoRoute", new {
cityID = (string.IsNullOrEmpty(cityID) ? "MB" : cityID ),
pnumbers = (string.IsNullOrEmpty(numbers) ? string.Empty : numbers) ,
pdays = (days == 0 ? 1000 : days),
ponlySpecial = onlySpecial});
}
When I frist access the controller :
and hit the submit button without entering/modifying any parameter, there are no errors :
but if I modify the third or the fourth parameter, I will have this error :
No route in the route table matches the supplied values.
I debuged the code, and at the line RedirectToRoute in HttpPost, every parameters are about the same except the parameter that I modified. I can't think of a reason why is this error happening!
If I enter/modified the second parameter (the second text box counting from top to bottom), I have no errors either!
Any help is greatly appreciated!
P/s : If this is not clarify enough for you because of my poor English, I can provide a screen video which records how I get the error!
You have pnumbers = "" in your route but it's not marked as an optional field (and you wouldn't be able to have it as optional if it's in the middle with required fields around it).
Try defaulting it to "0" or something.
Another alternative is to move this option to the end of the required parameters and mark it as optional like:
pnumbers = UrlParameter.Optional
It's worth installing route debugger if you are having routing issues as it adds a nice interface at the bottom of the page which shows which routes will trigger and which wont. It's essential with complex routes IMO.

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.

ASP.Net MVC: Request variables through Routes and View method

Basically if I wanted to make something a search page with paging I would need a url like:
/Topics/Index?search=hi&page=1
What I can't seem to figure out is how to:
A) Set a default route with no search and page 1
/Topics/Index?page=1 or even /Topics/Index?search=&page=1
B) use the View method to do the same
I do see that if I have a method on the control:
Index(String search, Int32? page)
And use the url:
/Topics/Index?search=hi&page=1 or /Topics/Index?search=hi
It gives me what I want in the method. I just need a way to get a default route for the Topic controller to create a default url with said request variables. I just don't think that
/Topics/Index/hi/1
Is conducive to a search url, mostly because there's no guarantee I'll have search terms or a page so it could end up like:
/Topics/Index/1
Anything you pass in the RouteValueDictionary that doesn't map to a part of your Url will get added as a querystring parameter. So you can do:
Url.GenerateUrl("Route", "Index", "Topics",
new RouteValueDictionary(new
{
page = this.Model.PageNumber,
search = this.Model.Search
});
So basically I resorted to handling the non values by setting up defaults on the controller. Not sure this is the best idea though.
In GLobal.asax:
routes.MapRoute
(
"TopicDefault",
"Topic/{action}",
new { controller = "Topic", action = "Index"}
);
On the Controller:
public ActionResult Index(Int32? parentForumId, Int32? pageNumber, Int32? amountToShow)
{
Int32 revisedPageNumber = pageNumber.HasValue ? pageNumber.Value : 0;
Int32 revisedAmountToShow = amountToShow.HasValue ? amountToShow.Value : 10;
Int32 revisedParentForumId = parentForumId.HasValue ? parentForumId.Value : 1;
IList<TopicCreateViewModel> modelValues =
Topic.GetListForGrid(revisedParentForumId, revisedPageNumber,
revisedAmountToShow, out realPage)
return View("Index", modelValues);
}

Resources