ASP.NET Mvc 5返回带有seo友好URL的视图

时间:2017-01-18 16:13:48

标签: asp.net asp.net-mvc seo asp.net-mvc-routing

我正在寻找一种基于Id返回视图的方法,但在返回时添加友好的url部分。

我知道当我有这些数据时,我可以传递id和名称,例如使用:

app

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

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);
}

如果我从SelCat操作重定向到只有ID的Index操作,我想搜索友好名称并返回视图。我希望网址有友好的网址部分。

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

如果仅提供ID,如何在我的控制器操作中返回视图时,如何使我的网址友好? 设置RouteData.Values。似乎没有将该部分添加到网址。

2 个答案:

答案 0 :(得分:2)

你必须在数据库中查看SEO友好的slu <之前重定向并将其包含在网址中,否则为时已​​晚:

Index

一旦你到达<div class="innerDiv" id="Scorecards"> <br>Scorecards<br> <a href="https://google.com"><img class="imgCustom" id="menuImages" src="http://image.flaticon.com/icons/svg/44/44357.svg"><div>Google</div></a> </div> <div class="innerDiv" id="Dashboards"> <br>Dashboards<br> <a href="https://abc.aspx"><img class="imgCustom" id="menuImages" src="http://icons.iconarchive.com/icons/aha-soft/3d-social/64/Online-writing-icon.png"><div>Microsoft</div></a> <a href="https://cde.aspx"><img class="imgCustom" id="menuImages" src="http://icons.iconarchive.com/icons/babasse/old-school/64/old-videos-icon.png"><div>Amazon</div></a> </div>行动,现在已经太晚了,无法改变客户端上显示的网址,除非你进行额外的重定向(当然这会是疯狂的)。

答案 1 :(得分:1)

您可以创建自定义RouteBase子类并将所有网址加载到缓存中。然后,您只需要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的网址。这将带您到名为Details的控制器上的CustomPageController操作(然后可以返回视图)。

  

BTW - 路由是“返回视图”的另一个问题,所以你的问题有点令人困惑。