2014-09-28 68 views
2

我正在研究为我计划创建的新网站设置数据库路由。我一直在看下面的教程,关于从数据库中利用friendlyUrls:ASP.Net WebForms路由多个目的地的单一路由

http://www.asp.net/web-forms/tutorials/aspnet-45/getting-started-with-aspnet-45-web-forms/url-routing

不过,我想用同样的路线结构,多个实体。含义:

mysite.com/{PlayerName}去player.aspx mysite.com/{TeamName}去team.aspx ...等等...

能够在实现发展的正确方向有人点这与asp.net。是否有可能使用内置的路由引擎,或者我应该为这个编码我自己的HTTPModule?

感谢 大卫

+1

路由如何知道'foo'是'mysite.com/foo'中的团队还是玩家?在您链接到的页面上,除了其他内容外,还说“{language} {country}/{action}”无效,因为路由无法知道语言的结束位置和国家的起始位置。你的想法有同样的问题。 – user1429080 2014-10-01 19:48:57

+0

这不可能使用System.Web.Routing - URL的结构看起来与路由引擎完全相同,那么它将如何知道URL引用的实体类型?为什么不能使用* mysite.com/teams/{teamname} *和* mysite.com/players/{playername} *代替? – sh1rts 2014-10-02 01:44:47

+0

感谢您的意见。我希望能够为团队和玩家提供相同的简单网址结构,以保持简洁。 我想这可能是可能实现,也许坐在路由引擎的前面,这样我自己的HTTP模块: 用户导航到:mysite.com/JoeBloggs, 我的HTTP模块,然后做一个数据库查询和然后某种server.transfer:mysite.com/players/JoeBloggs, URL路由然后相应地处理mysite.com/players/{playername}路线。 但是,这听起来过于复杂这个问题? – DavidReid 2014-10-02 13:26:51

回答

1

写两个constraints其返回布尔段是否是一个团队或不/播放器或不。

public class IsTeamConstraint : IRouteConstraint 
{ 
    public bool Match 
     (
      HttpContextBase httpContext, 
      Route route, 
      string parameterName, 
      RouteValueDictionary values, 
      RouteDirection routeDirection 
     ) 
    { 
     return SomeService.IsTeam(values["teamName"]); 
    } 
} 

public class IsPlayerConstraint : IRouteConstraint 
{ 
    public bool Match 
     (
      HttpContextBase httpContext, 
      Route route, 
      string parameterName, 
      RouteValueDictionary values, 
      RouteDirection routeDirection 
     ) 
    { 
     return SomeService.IsPlayer(values["playerName"]); 
    } 
} 

在页面路由中设置约束。

void RegisterCustomRoutes(RouteCollection routes) 
{ 
    routes.MapPageRoute(
     "Team", 
     "{teamName}", 
     "~/Team.aspx", 
     false, 
     null, 
     new RouteValueDictionary { { "isTeam", new IsTeamConstraint() } } 
    ); 
    routes.MapPageRoute(
     "Player", 
     "{playerName}", 
     "~/Player.aspx", 
     false, 
     null, 
     new RouteValueDictionary { { "isPlayer", new IsPlayerConstraint() } } 
    ); 
} 

现在,当一个页面被请求注册页面的路线将使用约束检查路由是有效的,如果它执行网页。

我还没有在ASP.Net Forms中尝试过这个,但是我的应用程序运行时使用ASP.Net MVC开发的约束。这两种类型的应用程序(Forms和MVC)共享通用路由逻辑。

+0

我测试了这种网页形式和开箱即用。 – Mihir 2014-10-10 09:37:42

+0

嗨,道歉,我认为赏金已经被授予,但我必须说这似乎是一个更好的方法。感谢您花时间提供帮助。 – DavidReid 2014-10-10 09:59:11

+0

谢谢,这很好,我刚刚在2天前回答了这个问题,并且好奇地知道它如何与真正的实现一起进行,所以我今天做到了:-) – Mihir 2014-10-10 10:08:40

0

正如其他人所指出的......这将是更最好不要使用两个球员和球队的这条路线。

这将是最好设置两条路线......

mysite.com/player/{PlayerName}

mysite.com/team/{TeamName}

这样就可以将所有“玩家”流量推送到Player.aspx和“团队”流量到Team.aspx,很好,很容易。

但是......如果您确实需要支持单一路由,我建议您将其添加为第三个选项,并使用301重定向到上述两个路由之一。

mysite.com/{PlayerOrTeamName} - > Route.aspx 让Route.aspx处理未映射到物理文件的请求。

然后你的Route.aspx代码需要作为404错误处理函数,但有一个catch。它将检查球员数据和球队数据完全匹配。如果它找到一个,它应该执行301永久重定向到正确的/玩家/或/团队/路线。

使用...

string strCorrectURL = RouteTable.Routes.GetVirtualPath(null, "player", new RouteValueDictionary { { "Name", strValue }}); 

    Response.StatusCode = 301; 
    Response.Status = "301 Moved Permanently"; 
    Response.AddHeader("Location", strCorrectURL); 
    Response.End(); 

这会给你一个单一路径的功能,但告诉搜索引擎索引更精确的路径。

您可以完全跳过RouteTable,只需将此代码放入默认的404处理程序。

1

我也不知道如何使用路由完成。但实现此目的的一种方法是使用URL重写。整个过程有几个步骤,制作起来相当简单。

  • 应用URL重写

你加在Global.asax下面的函数。

void Application_BeginRequest(object sender, EventArgs e) 
{ 
    //Here you will get exception 'Index was outside the bounds of the array' when loading home page, handle accordingly 
    string currentsegment = Request.Url.Segments[1]; 
    string RewritePath = ""; 

    if (IsTeam(currentsegment)) 
    { 
     RewritePath = "~/team.aspx?team=" + currentsegment; 
    } 

    if (IsPlayer(currentsegment)) 
    { 
     RewritePath = "~/player.aspx?player=" + currentsegment; 
    } 

    if (RewritePath != "") { 
     // Adding all query string items to the new URL 
     for (int I = 0; I <= Request.QueryString.Count - 1; I++) 
     { 
      RewritePath = RewritePath + "&" + Request.QueryString.Keys[I] + "=" + Request.QueryString[I]; 
     } 
     Context.RewritePath(RewritePath); 
    } 
} 

因此,如果URL已经是/some-title-here可以使用Request.Url.Segments阵列得到some-title-here部分。

然后根据你的代码检测这个标题是团队还是玩家。在任何情况下,您都可以通过调用Context.RewritePath(...)来更改内部URL。

一个重要的事情是,您需要手动添加所有查询字符串项目,以便将它们传递到您的页面。

此外,在您的代码Request.Url将知道重写的URL,而不是原来的。

快速测试它的方法是实现IsTeam(...)IsPlayer(...)函数如下。只有当这个代码打到/player-tasos~/player.aspx?player=player-tasos页面加载和当击中/team-stackoverflow页面加载页面。

private bool IsTeam(string segment) 
{ 
    return segment.StartsWith("team"); 
} 

private bool IsPlayer(string segment) 
{ 
    return segment.StartsWith("player"); 
} 

到目前为止,这种方法的工作原理,但它有一个主要问题。当有一个回发网址更改为您在Context.RewritePath(...)

  • 避免回传问题设置一个

为了避免这个问题,你需要添加到您的二期项目ASP.NET文件夹

  1. App_Browsers文件
  2. App_Code文件

在App_Code文件夹中创建文件FormRewriter.cs并添加以下代码(在我的演示根命名空间是WebFormsRewriting

using Microsoft.VisualBasic; 
using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.Data; 
using System.Diagnostics; 
using System.Web; 
using System.Web.UI; 

namespace WebFormsRewriting 
{ 
    public class FormRewriterControlAdapter : System.Web.UI.Adapters.ControlAdapter 
    { 
     protected override void Render(System.Web.UI.HtmlTextWriter writer) 
     { 
      base.Render(new RewriteFormHtmlTextWriter(writer)); 
     } 
    } 

    public class RewriteFormHtmlTextWriter : System.Web.UI.HtmlTextWriter 
    { 

     public RewriteFormHtmlTextWriter(HtmlTextWriter writer) 
      : base(writer) 
     { 
      this.InnerWriter = writer.InnerWriter; 
     } 

     public RewriteFormHtmlTextWriter(System.IO.TextWriter writer) 
      : base(writer) 
     { 
      base.InnerWriter = writer; 
     } 

     public override void WriteAttribute(string name, string value, bool fEncode) 
     { 
      // If the attribute we are writing is the "action" attribute, and we are not on a sub-control, 
      // then replace the value to write with the raw URL of the request - which ensures that we'll 
      // preserve the PathInfo value on postback scenarios 
      if ((name == "action")) 
      { 
       HttpContext Context = default(HttpContext); 
       Context = HttpContext.Current; 

       if (Context.Items["ActionAlreadyWritten"] == null) 
       { 
        // Because we are using the UrlRewriting.net HttpModule, we will use the 
        // Request.RawUrl property within ASP.NET to retrieve the origional URL 
        // before it was re-written. You'll want to change the line of code below 
        // if you use a different URL rewriting implementation. 

        value = Context.Request.RawUrl; 

        // Indicate that we've already rewritten the <form>'s action attribute to prevent 
        // us from rewriting a sub-control under the <form> control 

        Context.Items["ActionAlreadyWritten"] = true; 
       } 
      } 

      base.WriteAttribute(name, value, fEncode); 
     } 
    } 
} 

在App_Browsers文件夹中创建文件Form.browser,并添加以下代码片段。请注意,将Adapter的类名称与其名称空间放在一起。

<browsers> 
    <browser refID="Default"> 
     <controlAdapters> 
      <adapter controlType="System.Web.UI.HtmlControls.HtmlForm" 
        adapterType="WebFormsRewriting.FormRewriterControlAdapter" /> 
     </controlAdapters> 
    </browser> 
</browsers> 

就是这样。添加这两个文件将处理PostBack问题。如果您将FormRewriter.cs放在App_Code文件夹之外,它将不起作用。此外,这两个文件夹必须上传到生产服务器。

我已经在.NET 3.5和.NET 4.0中使用了这种方法多年,没有任何问题。今天,我也在.NET 4.5 Web Forms项目中测试了它,它没有问题。

以上所有的都是基于ScottGu的article对上述

2

我不知道为什么这么多人说这不能用路由完成 - 也许我没有得到的东西,但相同的逻辑,显然使接受的答案是一个有效的选项应该完全适用于自定义路由处理程序,例如IRouteHandler或从System.Web.Routing.RouteBase派生的东西。

你可以的方式增加 “经理人” 你RouteCollection(RouteTable.Routes):

routes.Add("MyRoutName", new MyCustomRouteBaseThing()) 

...或者:

routes.Add(new Route("whatever/{possiblySomething}", new RouteValueDictionary { 
     {"whatever", null} 
    }, new MyImplementationOfIRouteHandler())); 

......诸如此类,这取决于你需要。

如果你用RouteBase替代方案,例如覆盖GetRouteData()GetVirtualPath()和什么。我并不是说它比接受的答案更好,我只是不明白为什么布线应该被认为是不可行的。 (我是什么失踪?)

编辑:在我写了上述情况,“接受的答案”是关于URL的一个重写张贴TASOS K,谁的奖金也被奖励的时间。被接受的答案此后被重新分配。