繁体   English   中英

ASP.Net WebForms路由多个目标的单个路由

[英]ASP.Net WebForms Routing Single Route for Multiple Destinations

我正在考虑为我计划创建的新网站设置数据库路由。 我一直在关注以下有关利用数据库中的friendlyUrl的教程:

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?

谢谢大卫

我不确定为什么有这么多人说这不能通过路由来完成-也许我没有得到什么,但是显然使接受的答案成为有效选项的相同逻辑应该完全适用于自定义路由处理程序,例如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重写的内容,赏金也得到了奖励。 此后已重新分配接受的答案。

我也不知道如何使用路由来做到这一点。 但是,实现此目标的一种方法是改用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,而不是原始的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 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并添加以下代码段。 请注意,在此将适配器的类名及其名称空间放入。

<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窗体项目中对其进行了测试,并且可以正常使用。

以上所有内容均基于ScottGu关于该主题的文章

编写两个约束 ,这些约束将返回布尔值,无论段是否是团队/球员。

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表单中尝试过此操作,但我的应用程序运行时在ASP.Net MVC中开发了约束。 两种类型的应用程序(窗体和MVC)都共享通用的路由逻辑。

正如其他人指出的那样……最好不要对球员和球队都使用此路线。

最好设置两条路线...

mysite.com/player/{PlayerName}

mysite.com/team/{TeamName}

这样,您就可以轻松便捷地将所有“玩家”流量驱动到Player.aspx,并将“团队”流量驱动到Team.aspx。

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

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

然后,您的Route.aspx代码需要充当404错误处理程序,但要注意一个问题。它将检查Players数据和Teams数据是否完全匹配。 如果找到一个,则应执行301永久重定向到正确的/ player /或/ team /路线。

使用...

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM