路由是管理 web 应用程序 URL 的强大机制。它允许我们有好的,友好的网址,可以提供一个有用的见解,他们的意思是什么。除此之外,它还提供了不应该被丢弃的安全性和可扩展性特性。
路由是 MVC 的一个重要部分,但实际上是在 ASP.NET 3.5 SP1 被引入到 Web Forms 中的。它依赖于全局 Web.config 文件中配置的一个 ASP.NET 模块( UrlRoutingModule ),所以我们通常不需要担心它,还依赖于一个通过代码配置的路由表。我们不会详细讨论 ASP.NET 路由,但我们将探索它的两个可扩展性点:路由处理程序和路由约束。
路由处理程序是一个实际负责处理请求的类,由 IRouteHandler 接口的实现组成。ASP.NET 包含了一个 MVC 的实现(mvcrouthandler,但是你可以很容易地为 Web Forms 推出你自己的实现。那么,它有什么用呢?那么,您可以向其中添加自己的代码逻辑,并放心它将只应用于指定的路由。让我们看一个例子:
代码示例 70
var customRoute = new Route("{controller}/{action}/{id}",
new RouteValueDictionary(new { controller = "Home", action = "Index",
id = UrlParameter.Optional }), new MyRouteHandler());
RouteTable.Routes.Add(customRoute);
我们正在注册一个普通的路由,唯一的区别是我们正在传递一个定制的路由处理程序, MyRouteHandler
,下面给出了它的一个示例实现:
代码示例 71
class MyRouteHandler : MvcRouteHandler
{
protected override IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
var tenant = TenantsConfiguration.GetCurrentTenant();
//do something with the current tenant
return base.GetHttpHandler(requestContext);
}
}
该处理程序将适用于所有匹配“**{controller}/{action}/{id}**”,
给定网址模板的请求,无论当前租户是谁。通过调用它的基础实现,一切都将按预期运行。然而,我们可以根据某些条件来限制我们的路线——输入路线限制。
在我们继续之前,重要的是要注意这些例子是针对 MVC 的,但是我们可以为 Web Forms 做一些类似的事情;唯一不同的是基类 PageRouteHandler ,在本例中:
代码示例 72
class PageRouteHandler : PageRouteHandler
{
public WebFormsRouteHandler(String virtualPath) : base(virtualPath) { }
public override IHttpHandler GetHttpHandler(RequestContext
requestContext)
{
var tenant = TenantsConfiguration.GetCurrentTenant();
//do something with the current tenant
return base.GetHttpHandler(requestContext);
}
}
此外,注册略有不同:
代码示例 73
var customRoute = new Route(String.Empty, new WebFormsRouteHandler
("~/Default.aspx"));
RouteTable.Routes.Add(customRoute);
一个路由约束需要实现框架接口 IRouteConstraint 。当这样的实现应用于路由时,urrlroutingmodule将首先检查它,看当前请求是否可以匹配到路由。下面是我们如何指定一个(或多个)路由约束:
代码示例 74
var customRoute = new Route("{controller}/{action}/{id}",
new RouteValueDictionary(new { controller = "Home", action = "Index",
id = UrlParameter.Optional }), new RouteValueDictionary(
new Dictionary<String, Object> { { "my", new MyRouteConstraint() } }),
new MyRouteHandler());
RouteTable.Routes.Add(customRoute);
最后,一个考虑到当前租户的约束示例:
代码示例 75
class MyRouteConstraint : IRouteConstraint
{
public Boolean Match(HttpContextBase httpContext, Route route,
String parameterName, RouteValueDictionary values,
RouteDirection routeDirection)
{
var tenant = TenantsConfiguration.GetCurrentTenant();
//do some check with the current tenant and return true or false
return true;
}
}
基于当前租户限制访问的另一个选项在于 IIS 重写模块。这个模块是从微软免费下载的,它允许我们指定控制访问和重定向请求的规则,所有这些都来自 Web.config 文件。基本上,重写规则包括:
图 14: IIS 重写模块规则组件
rule
元素定义了 name
,如果规则应该是 enabled
或者不是,并且模式匹配语法用在所有模糊表达式属性( patternSyntax
)中,这可以是:
- 通配符:没有正则表达式,只有通配符(*)匹配
- ECMAScript : ECMAScript 标准正则表达式
- 精确匹配:精确匹配
代码示例 76
<rewrite>
<rules>
<rule enabled="true" patternSyntax="ECMAScript"
name="ABC.COM only">
<!-- rest goes here -->
</rule>
</rules>
</rewrite>
然后是 match
元素。在其中我们指定一个 URL 模式( url
),可以是特定的页面,如果匹配应该以不区分大小写的方式进行( ignoreCase
),即使匹配应该被还原( negate
),也就是接受不匹配模式的 URL:
代码示例 77
<rewrite>
<rules>
<rule enabled="true" patternSyntax="ECMAScript"
name="ABC.COM only">
<match url="\/abc\.com\/.*" ignoreCase="true" />
<!-- rest goes here -->
</rule>
</rules>
</rewrite>
然后我们可以添加任意数量的**conditions
**;这些通常通过对照以下匹配类型之一检查服务器变量 ( input
)来构建:
Pattern
: 图案,默认IsFile
:检查文件是否存在IsDirectory
:检查目录是否存在
条件可以有不同的评价( logicalGrouping
):
MatchAll
:所有规则都需要正面评价(默认)。MatchAny
:至少有一个规则是正的。
条件也可以被否定,如本例所示:
代码示例 78
<rewrite>
<rules>
<rule enabled="true" patternSyntax="ECMAScript"
name="ABC.COM only">
<match url="\/abc\.com\/.*" ignoreCase="true" />
<conditions>
<add input="{HTTP_HOST}" matchType="Pattern"
pattern="abc.com" negate="true" />
</conditions>
<!-- rest goes here -->
</rule>
</rules>
</rewrite>
服务器变量或多或少是 web 服务器的标准;它们在 RFC 3875 中定义,包括:
HTTP_HOST
:请求中发送的服务器主机名HTTP_REFERER
:浏览器来源的 URLHTTP_USER_AGENT
:用于访问页面的浏览器HTTPS
:请求是否使用 HTTPSPATH_INFO
:跟随实际服务器资源的路径QUERY_STRING
:请求的查询字符串REMOTE_ADDR
:客户地址REMOTE_HOST
:客户端的主机名REMOTE_USER
:认证用户
最后,我们指定一个动作,如果匹配和条件成功评估,则触发该动作。操作可以是以下之一:
AbortRequest
:停止当前请求;用于拒绝访问None
:什么都不做Rewrite
:内部将请求改写为不同的东西Redirect
:将浏览器重定向到不同的资源CustomResponse
:发送自定义的 HTTP 头、状态码和子码
此示例拒绝除 abc.com
租户之外的所有人访问 /abc.com
下的所有内容:
代码示例 79
<rewrite>
<rules>
<rule enabled="true" patternSyntax="ECMAScript"
name="ABC.COM only">
<match url="\/abc\.com\/.*" ignoreCase="true" />
<conditions>
<add input="{HTTP_HOST}" matchType="Pattern"
pattern="abc.com" negate="true"/>
</conditions>
<action type="AbortRequest" statusCode="401"
statusDescription="Denied for others than ABC.COM" />
</rule>
</rules>
</rewrite>
最后,我们可以指定一个或多个服务器变量( serverVariables
)。这些可以用于在条件中被引用,或者将某种信息传递给处理请求的处理程序。例如,如果我们要使用主机头策略捕获租户,我们可以:
代码示例 80
<rewrite>
<rules>
<rule enabled="true" patternSyntax="ECMAScript"
name="ABC.COM only">
<serverVariables>
<set name="TENANT" value="{HTTP_HOST}" />
</serverVariables>
<match url="\/abc\.com\/.*" ignoreCase="true" />
<conditions>
<add input="{TENANT}" matchType="Pattern"
pattern="abc.com" negate="true"/>
</conditions>
<action type="AbortRequest" statusCode="401"
statusDescription="Denied for others than ABC.COM" />
</rule>
</rules>
</rewrite>
IIS 重写模块还具有一个捕获机制,允许我们检索匹配正则表达式的部分内容。例如,如果我们要使用查询字符串策略来确定租户,我们可以使用:
代码示例 81
<conditions>
<add input="{QUERY_STRING}" pattern="&?tenant=(.+)" />
<!-- the tenant passed in the query string will be in {C:1} -->
</conditions>
<action type="Rewrite" url="/tenant/{C:1}"/>
我们将使用 {R:*n*}
作为在 match
元素中捕获的引用,使用 {C:*n*}
作为在 conditions
中捕获的引用,其中 0 将返回整个匹配字符串以及在“()”
中捕获的每个元素的 1,…,n。这个简单的示例将“**tenant=something**”
的所有请求重定向到“**/tenant/something**”.
例如,如果您想将某个租户的所有请求从一个资源重定向到另一个资源,即不应该被某个客户访问的资源,那么这个示例可能会很有用。