Skip to content

Files

Latest commit

60c9d10 · Jan 8, 2022

History

History
268 lines (198 loc) · 11.9 KB

File metadata and controls

268 lines (198 loc) · 11.9 KB

七、路由

简介

路由是管理 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 重写模块

基于当前租户限制访问的另一个选项在于 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 :浏览器来源的 URL
  • HTTP_USER_AGENT :用于访问页面的浏览器
  • HTTPS :请求是否使用 HTTPS
  • PATH_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="&amp;?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**”.例如,如果您想将某个租户的所有请求从一个资源重定向到另一个资源,即不应该被某个客户访问的资源,那么这个示例可能会很有用。