I'm implementing a site which has a search that can be executed via Controller methods that are accessed from two different routes. One hangs off the default route (using either Post data or query string values for the search parameters) and one is an SEO optimisation url which takes a location and phrase via two route values. The second route looks like this:
routes.MapRoute("SEOSearch", "Search/{seoLocation}/{seoSearchString}",
new { controller = "Search",
action = "SEOResults",
seoL开发者_开发问答ocation = (string)null,
seoSearchString = (string)null });
You might be wondering why I have two different routes - it's because the search offers many other parameters than just location and phrase - but I want the SEO'd urls to include those two in the path, rather than using the query string.
As I say, the first route is the default /controller/action/id
route, and the correct controller/action for that is "Search" and "Index".
In the end, both actions execute the same search operation in the controller, and both will render their results using the Index view, since their result models are identical.
On the index view I use a partial view for the search terms, another partial for the results and another for the paging.
The problem I'm having is getting the paging control to render the correct link to launch the current search for the next page using the same URL format as the current request.
What I want
So, assuming you've navigated to /Search?Location=[location]&Phrase=[phrase]
, I want Page 2's link generated by the pager to be /Search?Location=[location]&Phrase=[phrase]&Page=2
.
However, if you've launched the search with /Search/[location]/[phrase]
, I want Page 2's link to be /Search/[location]/[phrase]?Page=2
.
What I've got
The closest I've got is this:
<%= Html.RouteLink("Previous Page",
RouteHelpers.Combine(ViewContext.RouteData.Values,
new RouteValueDictionary() { { "Page", Model.Results.PageNo + 1}})) %>
Where RouteHelpers.Combine
is an extension that I've written that takes two objects and merges them into one RouteValueDictionary. By taking the RouteValues for the current request, I'm able to persist the current Controller and Action name (without having to know what they are) - however this misses some important information from ModelState - i.e. any extra search parameters that were provided - i.e. it works if the current Url is /Search/London/Widgets
, but if it's /Search/London/Widgets?PageSize=50
then the PageSize
parameter doesn't get persisted into the outgoing link.
Even worse, if it's a non-SEO'd url - i.e. /Search?Location=London&Phrase=Widgets
, the outgoing url simply becomes /Search?Page=x
.
My search parameters are read from the request into a model type, that is then fed to both the host page, and to the pager itself, so in theory I could simply always generate them from that - but I end up with all the parameters in the url, even when they are default values (therefore they do not need to be supplied) - so the url looks ugly.
How do I achieve what I want!? I'm feeling like I know nothing about MVC all of sudden!
You don't have to use your RouteHelper
to combine route values. Those values are combined with your anonymous object automatically when you use ActionLink instead. All existing values will be overwritten and new ones added. This call does that for you:
RouteValueDictionary values = RouteValuesHelpers.MergeRouteValues(
actionName,
controllerName,
requestContext.RouteData.Values,
routeValues,
includeImplicitMvcValues); // true for ActionLink; false for RouteLink
Where routeValues
are your values from anonymous object. They get merged to requestContext.RouteData.Values
.
So you can still use:
Html.ActionLink(
"whatever",
this.ViewContext.RouteData.Values["action"],
this.ViewContext.RouteData.Values["controller"],
new { Page = /* whatever needs to be */ },
null)
Null at the end is mandatory, so it doesn't confuse anonymous object with HTML attributes.
But you do have routing problems unless you also put a route constraint on your default route to only use certain actions, otherwise your /Search/SeoLocation/SeoSearchString
will still be handled by your default route, by SeoLocation
becoming your action and SeoSearchString
the id
.
Anyway. If your code would be correct all those values should be inside your route values dictionary and propagated to your links as expected.
精彩评论