我正在研究MVC 5(asp)应用程序。我得到的一个要求是有多个导航路径指向同一目标页面。问题是根据用户的导航历史记录在帖子之后返回到上一页。 让我们考虑三个网页的基本情景
Customers / ShowAll - >显示所有客户的列表
客户/搜索 - >根据搜索(姓名,国家......)显示客户列表
Customers / Update / 1134 - >显示特定客户的更新页面(即customer_id = 1134)
所以导航路径就像这样
ShowAll - >更新
或
搜索 - >更新
如果用户导航到“Customers / Search”然后“Customers / Update / 1134”,更新客户信息并保存数据,我希望服务器重定向到“客户/搜索”页面,因为它是路径用户使用。 这是一个非常基本的情况,但它可能更复杂,如返回许多页面并始终返回先前访问过的页面(如果没有页面与历史记录匹配则为默认页面)。
到目前为止我做了什么
我创建了一个原型,用于跟踪服务器端用户的导航历史记录。它使用客户端上的会话存储为当前浏览器选项卡提供唯一ID。在每个页面卸载时,它会将浏览器选项卡ID添加到cookie中。然后在服务器端有一个字典(在会话中),其中选项卡ID(从cookie中提取)作为密钥,访问URL列表作为值。当前URL将添加到URL列表中。我发现这个解决方案有效,但它有一些缺陷。
最后一件事是URL的字典仅限于访问的最后30页,因为我想限制服务器内存。这对我的问题并不重要,但我觉得要提一下,因为我相信你们中的一些人可能会看到所有用户都保留历史记录中所有标签的所有页面的问题。
我还想到了一个类似的解决方案,使用cookie来传输每个请求访问的最后30个页面,并让服务器在需要时解析这个历史记录。仅保留应用程序域中的页面。这将解决会话超时后的持久性问题,但它会向服务器引入更多的处理,因为将在每个请求中解析历史记录。 我想知道是否有更好的解决方案来根据导航历史重定向用户。也许在MVC 5中有一个我不知道的内置功能。
感谢您的任何建议。
问候。
答案 0 :(得分:0)
使用会话状态(正如您所发现的)不是解决此问题的非常好的解决方案,因为:
使其在100%的时间内工作的唯一方法是将所有导航标识符信息放入URL中,以便系统可以确定如何构建导航链接。
MVC 5中没有内置功能,但您可以使用MvcSiteMapProvider来解决问题。它包含Menu和SiteMapPath的HTML帮助程序,它们就像一个痕迹路径。
@Html.MvcSiteMap().Menu()
@Html.MvcSiteMap().SiteMapPath()
它的工作原理不同 - 它将单个共享的节点层次结构(站点地图)加载到内存中。然后,每个进入的请求都匹配其中一个节点,并使用该映射来确定如何在HTML帮助程序中构建链接。根本没有使用会话状态。
让它在您的方案中运行的技巧是使Customers/Update/1134
页面可用于2个不同的URL。然后,您可以配置2个不同的节点层次结构,它将根据路由信息知道要匹配哪个。
例如,您可以添加一个额外的路线值,表示您要导航的页面是搜索页面。
@Html.ActionLink("Customer 1134", "Update", "Customers", new { source = "Search" }, null)
默认情况下,这会构建一个类似Customers/Update/1134?source=Search
的网址。您可以通过调整路线配置使其看起来更漂亮。
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "SearchSource",
url: "Search/{controller}/{action}/{id}",
defaults: new { source = "Search", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "ShowAllSource",
url: "ShowAll/{controller}/{action}/{id}",
defaults: new { source = "ShowAll", controller = "Home", action = "Index", id = UrlParameter.Optional }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
}
现在使用上面显示的相同ActionLink,您将获得网址/Search/Customers/Update/1134
。那更好。请注意,当您将ActionLink
放在ShowAll
页面上时,它应该是这样的:
@Html.ActionLink("Customer 1134", "Update", "Customers", new { source = "ShowAll" }, null)
然后在MvcSiteMapProvider
中设置节点配置时,需要创建2个不同的父节点,如下所示。
<mvcSiteMapNode title="Home" controller="Home" action="Index">
<!-- Additional nodes here -->
<mvcSiteMapNode title="Search" controller="Customers" action="Search">
<mvcSiteMapNode title="Update Customer" controller="Customers" action="Update" source="Search" preservedRouteParameters="id"/>
</mvcSiteMapNode>
<mvcSiteMapNode title="Show All Customers" controller="Customers" action="ShowAll">
<mvcSiteMapNode title="Update Customer" controller="Customers" action="Update" source="ShowAll" preservedRouteParameters="id"/>
</mvcSiteMapNode>
<!-- Additional nodes here -->
</mvcSiteMapNode>
然后,您将获得完整的导航解决方案:
/Customers/ShowAll | Home > Show All Customers
/ShowAll/Customers/Update/1134 | Home > Show All Customers > Update Customer
/Customers/Search | Home > Search
/Search/Customers/Update/1134 | Home > Search > Update Customer
当然,这只是一个例子。您可以使URL和导航链接以您想要的任何方式显示。
最后,重定向回用户来自的位置。这很容易,因为MvcSiteMapProvider
会跟踪父节点。
[HttpPost]
public ActionResult Update(CustomerModel model)
{
// Update customer here...
var currentNode = this.GetCurrentSiteMapNode();
if (currentNode != null)
{
var parentNode = currentNode.ParentNode;
if (parentNode != null)
{
return Redirect(parentNode.Url);
}
}
return View(model);
}
您可能希望从原始父页面存储一些其他信息(排序顺序,搜索项等),在这种情况下,您需要通过“更新”页面传递这些参数,然后以某种方式返回到父页面。
一种方法是使用会话,然后在进入重定向页面时使用某些逻辑默认行为。
另一种(更简单的)方法是将它们作为参数(查询字符串或路由值)添加到“客户更新”页面的URL中,以便它们自动构建到返回URL中。由于您有2条不同的路线,因此您只需将信息添加到适当的路线即可。您只需要确保“搜索”页面和“客户更新”页面的路由都包含它们,以便正确构建父URL。
完全披露:我是
MvcSiteMapProvider
项目的主要撰稿人。
另见: