2017-01-18 33 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的操作。

順便說一句,路由是一個單獨的關注「返回視圖」,所以你的問題有點混亂。