2014-09-28 96 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,誰的獎金也被獎勵的時間。被接受的答案此後被重新分配。