从使用当前路由值和模型状态的分页用户控件生成 ActionLink/RouteLink
我正在实现一个站点,该站点具有可以通过从两个不同路径访问的控制器方法执行的搜索。一种是挂在默认路由上(使用发布数据或查询字符串值作为搜索参数),另一种是 SEO 优化 URL,它通过两个路由值获取位置和短语。第二条路线如下所示:
routes.MapRoute("SEOSearch", "Search/{seoLocation}/{seoSearchString}",
new { controller = "Search",
action = "SEOResults",
seoLocation = (string)null,
seoSearchString = (string)null });
您可能想知道为什么我有两条不同的路线 - 这是因为搜索提供了许多其他参数,而不仅仅是位置和短语 - 但我希望 SEO 网址在路径中包含这两个,而不是使用查询字符串。
正如我所说,第一个路由是默认的 /controller/action/id
路由,正确的控制器/操作是“Search”和“Index”。
最后,两个操作在控制器中执行相同的搜索操作,并且都将使用索引视图呈现其结果,因为它们的结果模型是相同的。
在索引视图上,我使用一个部分视图来显示搜索词,另一个部分视图用于结果,另一个部分视图用于分页。
我遇到的问题是让分页控件呈现正确的链接,以使用与当前请求相同的 URL 格式启动对下一页的当前搜索。
我想要什么
那么,假设您已导航到 /Search?Location=[location]&Phrase=[phrase]
,我希望寻呼机生成的第 2 页的链接为 /Search ?Location=[位置]&Phrase=[phrase]&Page=2
。
但是,如果您使用 /Search/[location]/[phrase]
启动搜索,我希望第 2 页的链接为 /Search/[location]/[phrase]?Page =2
。
我得到的
最接近的是:
<%= Html.RouteLink("Previous Page",
RouteHelpers.Combine(ViewContext.RouteData.Values,
new RouteValueDictionary() { { "Page", Model.Results.PageNo + 1}})) %>
其中 RouteHelpers.Combine
是我编写的一个扩展,它接受两个对象并将它们合并到一个 RouteValueDictionary 中。通过获取当前请求的 RouteValues,我能够保留当前的控制器和操作名称(无需知道它们是什么) - 但是这会错过来自 ModelState 的一些重要信息 - 即提供的任何额外搜索参数 - 即如果当前网址为 /Search/London/Widgets
,则有效,但如果为 /Search/London/Widgets?PageSize=50
,则 PageSize
> 参数不会保留到传出链接中。
更糟糕的是,如果它是非 SEO 网址 - 即 /Search?Location=London&Phrase=Widgets
,则传出网址将简单地变为 /Search?Page=x
。
我的搜索参数从请求中读取到模型类型中,然后将其馈送到主机页面和寻呼机本身,因此理论上我可以简单地始终从中生成它们 - 但我最终得到了所有参数url,即使它们是默认值(因此不需要提供它们) - 所以 url 看起来很难看。
我怎样才能达到我想要的目标!?我突然感觉自己对MVC一无所知!
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",
seoLocation = (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!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
您不必使用
RouteHelper
来组合路由值。当您使用 ActionLink 时,这些值会自动与您的匿名对象组合。所有现有值将被覆盖并添加新值。此调用会为您执行此操作:其中
routeValues
是来自匿名对象的值。它们被合并到requestContext.RouteData.Values
。所以你仍然可以使用:
末尾的 Null 是强制的,这样就不会混淆匿名对象和 HTML 属性。
但是您确实存在路由问题,除非您还在默认路由上设置了路由限制以仅使用某些操作,否则您的
/Search/SeoLocation/SeoSearchString
仍将由您的默认路由处理,即SeoLocation
成为您的操作,SeoSearchString
成为id
。反正。如果您的代码正确,所有这些值都应该位于您的路由值字典内,并按预期传播到您的链接。
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:Where
routeValues
are your values from anonymous object. They get merged torequestContext.RouteData.Values
.So you can still use:
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, bySeoLocation
becoming your action andSeoSearchString
theid
.Anyway. If your code would be correct all those values should be inside your route values dictionary and propagated to your links as expected.