ASP.Net WebForms路由多个目的地的单一路由

时间:2014-09-28 20:22:11

标签: asp.net webforms routing

我正在研究为我计划创建的新网站设置数据库路由。关于从数据库中使用friendlyUrls,我一直在查看以下教程:

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代码?

由于 大卫

4 个答案:

答案 0 :(得分: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()));

... Etcetera,视您的需要而定。

例如,如果您使用RouteBase替代方法,请覆盖GetRouteData()GetVirtualPath()等等。我并不是说它不一定比接受的答案更好,我只是不明白为什么路由应该被视为不可行。 (我错过了什么?)

编辑: 在我编写上述内容时,接受了答案"是由Tasos K发布的关于URL重写的那个,赏金也得到奖励。已经接受的答案已被重新分配。

答案 1 :(得分:1)

我也不知道如何使用路由完成此操作。但实现此目的的一种方法是使用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);
    }
}

因此,如果网址为/some-title-here,您可以使用some-title-here数组获取Request.Url.Segments部分。

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

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

此外,在您的代码中,Request.Url将知道重写的网址,而不是原始网址。

快速测试方法是实施IsTeam(...)IsPlayer(...)功能,如下所示。点击 / player-tasos 时只有这段代码加载~/player.aspx?player=player-tasos页面,当点击 / team-stackoverflow 时,会加载~/team.aspx?team=team-stackoverflow页面。

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

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

到目前为止,这种方法有效,但它有一个主要问题。当有PostBack时,URL会更改为您在Context.RewritePath(...)

中设置的URL
  • 避免回发问题

要避免此问题,您需要向项目中添加两个ASP.NET文件夹

  1. App_Browsers文件
  2. App_Code文件
  3. 在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&#39;}关于该主题的内容

答案 2 :(得分: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)共享公共路由逻辑。

答案 3 :(得分:0)

正如其他人所指出的那样......最好不要将此路线用于球员和球队。

最好设置两条路线......

mysite.com/player/ {PlayerName}

mysite.com/team/ {TeamName}

通过这种方式,您可以将所有“玩家”流量驱动到Player.aspx,并将“团队”流量驱动到Team.aspx,非常简单。

然而......如果您真的需要支持单一路线,我建议您将其添加为第三个选项,并使用301重定向到上述两个路线之一。

mysite.com/ {PlayerOrTeamName} - &gt; Route.aspx 让Route.aspx处理不映射到物理文件的请求。

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

使用...

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处理程序中。