2017-01-18 34 views
-1

我正在寻找一种方法来返回基于Id的视图,但返回时添加友好的url部分。ASP.NET Mvc 5返回使用搜索引擎友好网址查看

我知道当我有这个数据时,我可以传入id和name,例如,通过使用:

Url.Action("Index", "Cat", new { id = model.ID, seoname = model.SEO }); 


[AllowAnonymous] 
[Route("Cat/{id?}/{seoname?}")] 
public ActionResult Index(int? id = null, string seoname = null) { 
    // do something with id and create viewmodel 

    // in case I get redirect from the SelCat actionresult: 
    if (id.HasValue and string.IsNullOrEmpty(seoname)) { 
     // look in the database for title by the given id 
     string seofriendlyTitle = ...; 
     RouteData.Values.AddOrSet("seoname", seofriendlyTitle); 
    } 

    return View(viewmodel); 
} 

上面的这段代码不是问题。当我提交表单(下拉列表),我只有可用的Id时,会出现问题。

[HttpPost] 
[AllowAnonymous] 
[Route("Cat/SelCat/{form?}")] 
public ActionResult SelCat(FormCollection form) 
{ 
    string selectedValues = form["SelectedCat"]; 
    // ... 
    int id = selectedCatID; 
    return RedirectToAction("Index", new { id = id }); 
} 

在情况下,我从SelCat行动索引操作重定向只有一个ID我想搜索的seofriendly名称和返回视图时。我希望该网址具有友好的网址部分。

// in case I get redirect from the SelCat actionresult: 
    if (id.HasValue and string.IsNullOrEmpty(seoname)) { 
     // look in the database for title by the given id 
     string seofriendlyTitle = ...; 
     RouteData.Values.AddOrSet("seoname", seofriendlyTitle); // <-- does not alter url 
    } 

我怎样才能让我的网址搜索引擎友好的回国在我的控制器动作视图时,当只给出一个ID? 设置RouteData.Values。似乎并未将该部分添加到网址中。

回答

1

您将在数据库中搜索引擎友好的蛞蝓看之前偏转与包括它的URL,否则为时已​​晚:

[HttpPost] 
[AllowAnonymous] 
[Route("Cat/SelCat/{form?}")] 
public ActionResult SelCat(FormCollection form) 
{ 
    string selectedValues = form["SelectedCat"]; 
    // ... 
    int id = selectedCatID; 

    // look in the database for title by the given id 
    string seofriendlyTitle = ...; 
    return RedirectToAction("Index", new { id = id, seoname = seofriendlyTitle }); 
} 

一旦你已经达到了Index诉讼中,已经太迟了,无法改变客户端上显示的网址,除非您进行额外的重定向(这当然会是疯狂的)。

+0

由于逻辑我需要做索引操作的数据库查询,但现在我需要在SelCat操作中的第二个数据库查询。该死的! 我认为这可以在一个行动中完成。 – juFo

+0

鉴于您是通过id查询的,任何像样的数据库都应该可以正常查询主键。另外,如果您担心性能,可以添加一些缓存层。 –

1

您可以创建自定义RouteBase子类,并将所有URL加载到缓存中。然后,你需要的是id(大概基于主键)来查找URL。请注意,您可以制作id或者Guid(如图所示)或int

public class PageInfo 
{ 
    // VirtualPath should not have a leading slash 
    // example: events/conventions/mycon 
    public string VirtualPath { get; set; } 
    public Guid Id { get; set; } 
} 

public class CustomPageRoute 
    : RouteBase 
{ 
    private object synclock = new object(); 

    public override RouteData GetRouteData(HttpContextBase httpContext) 
    { 
     RouteData result = null; 

     // Trim the leading slash 
     var path = httpContext.Request.Path.Substring(1); 

     // Get the page that matches. 
     var page = GetPageList(httpContext) 
      .Where(x => x.VirtualPath.Equals(path)) 
      .FirstOrDefault(); 

     if (page != null) 
     { 
      result = new RouteData(this, new MvcRouteHandler()); 

      // Optional - make query string values into route values. 
      this.AddQueryStringParametersToRouteData(result, httpContext); 

      // TODO: You might want to use the page object (from the database) to 
      // get both the controller and action, and possibly even an area. 
      // Alternatively, you could create a route for each table and hard-code 
      // this information. 
      result.Values["controller"] = "CustomPage"; 
      result.Values["action"] = "Details"; 

      // This will be the primary key of the database row. 
      // It might be an integer or a GUID. 
      result.Values["id"] = page.Id; 
     } 

     // IMPORTANT: Always return null if there is no match. 
     // This tells .NET routing to check the next route that is registered. 
     return result; 
    } 

    public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values) 
    { 
     VirtualPathData result = null; 

     PageInfo page = null; 

     // Get all of the pages from the cache. 
     var pages = GetPageList(requestContext.HttpContext); 

     if (TryFindMatch(pages, values, out page)) 
     { 
      if (!string.IsNullOrEmpty(page.VirtualPath)) 
      { 
       result = new VirtualPathData(this, page.VirtualPath); 
      } 
     } 

     // IMPORTANT: Always return null if there is no match. 
     // This tells .NET routing to check the next route that is registered. 
     return result; 
    } 

    private bool TryFindMatch(IEnumerable<PageInfo> pages, RouteValueDictionary values, out PageInfo page) 
    { 
     page = null; 
     Guid id = Guid.Empty; 

     // This example uses a GUID for an id. If it cannot be parsed, 
     // we just skip it. 
     if (!Guid.TryParse(Convert.ToString(values["id"]), out id)) 
     { 
      return false; 
     } 

     var controller = Convert.ToString(values["controller"]); 
     var action = Convert.ToString(values["action"]); 

     // The logic here should be the inverse of the logic in 
     // GetRouteData(). So, we match the same controller, action, and id. 
     // If we had additional route values there, we would take them all 
     // into consideration during this step. 
     if (action == "Details" && controller == "CustomPage") 
     { 
      page = pages 
       .Where(x => x.Id.Equals(id)) 
       .FirstOrDefault(); 
      if (page != null) 
      { 
       return true; 
      } 
     } 
     return false; 
    } 

    private void AddQueryStringParametersToRouteData(RouteData routeData, HttpContextBase httpContext) 
    { 
     var queryString = httpContext.Request.QueryString; 
     if (queryString.Keys.Count > 0) 
     { 
      foreach (var key in queryString.AllKeys) 
      { 
       routeData.Values[key] = queryString[key]; 
      } 
     } 
    } 

    private IEnumerable<PageInfo> GetPageList(HttpContextBase httpContext) 
    { 
     string key = "__CustomPageList"; 
     var pages = httpContext.Cache[key]; 
     if (pages == null) 
     { 
      lock(synclock) 
      { 
       pages = httpContext.Cache[key]; 
       if (pages == null) 
       { 
        // TODO: Retrieve the list of PageInfo objects from the database here. 
        pages = new List<PageInfo>() 
        { 
         new PageInfo() 
         { 
          Id = new Guid("cfea37e8-657a-43ff-b73c-5df191bad7c9"), 
          VirtualPath = "somecategory/somesubcategory/content1" 
         }, 
         new PageInfo() 
         { 
          Id = new Guid("9a19078b-2d7e-4fc6-ae1d-3e76f8be46e5"), 
          VirtualPath = "somecategory/somesubcategory/content2" 
         }, 
         new PageInfo() 
         { 
          Id = new Guid("31d4ea88-aff3-452d-b1c0-fa5e139dcce5"), 
          VirtualPath = "somecategory/somesubcategory/content3" 
         } 
        }; 

        httpContext.Cache.Insert(
         key: key, 
         value: pages, 
         dependencies: null, 
         absoluteExpiration: System.Web.Caching.Cache.NoAbsoluteExpiration, 
         slidingExpiration: TimeSpan.FromMinutes(15), 
         priority: System.Web.Caching.CacheItemPriority.NotRemovable, 
         onRemoveCallback: null); 
       } 
      } 
     } 

     return (IEnumerable<PageInfo>)pages; 
    } 
} 

您可以像这样使用MVC注册路线。

routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); 

// Case sensitive lowercase URLs are faster. 
// If you want to use case insensitive URLs, you need to 
// adjust the matching code in the `Equals` method of the CustomPageRoute. 
routes.LowercaseUrls = true; 

routes.Add(
    name: "CustomPage", 
    item: new CustomPageRoute()); 

routes.MapRoute(
    name: "Default", 
    url: "{controller}/{action}/{id}", 
    defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional } 
); 

通过上述注册,您可以使用@Html.ActionLink("A link to a page", "Details", "CustomPage", new { id = "9a19078b-2d7e-4fc6-ae1d-3e76f8be46e5" }, null)生成基于ID的URL。这将引导您在名为CustomPageController(然后可以返回视图)的控制器上执行名为Details的操作。

顺便说一句,路由是一个单独的关注“返回视图”,所以你的问题有点混乱。