Using RewriteMap

本文档是对mod_rewrite reference documentation的补充。它描述了RewriteMap指令的用法,并提供了各种RewriteMap类型的示例。

Warning

请注意,这些示例中的许多示例在您的特定服务器配置中都不会起作用,因此,理解它们,而不只是将示例剪切并粘贴到您的配置中,这一点很重要。

Introduction

RewriteMap指令定义了一个外部函数,可以在RewriteRuleRewriteCond指令的上下文中调用该外部函数以执行过于复杂或过于专业化而无法仅由正则表达式执行的重写。查找的来源可以是以下各节中列出的任何类型,并且可以在RewriteMap参考文档中进行枚举。

RewriteMap指令的语法如下:

RewriteMap MapName MapType:MapSource

MapName是您分配给 Map 的任意名称,以后将在指令中使用。参数通过以下语法传递给 Map:

** ${ * MapName * : * LookupKey * }
${ * MapName * : * LookupKey * | * DefaultValue * } **

当发生这种构造时,将查询 Map* MapName 并查找键 LookupKey 。如果找到了键,则 Map 函数构造将被 SubstValue 替代。如果未找到键,则用 DefaultValue 替换,如果未指定 DefaultValue *,则用空字符串替换。

例如,您可以将RewriteMap定义为:

RewriteMap examplemap "txt:/path/to/file/map.txt"

然后,您将可以在RewriteRule中使用此 Map,如下所示:

RewriteRule "^/ex/(.*)" "${examplemap:$1}"

如果在 Map 中找不到任何内容,则可以指定默认值:

RewriteRule "^/ex/(.*)" "${examplemap:$1|/not_found.html}"

Per-directory and .htaccess context

RewriteMap伪指令不能在<Directory>节或.htaccess文件中使用。您必须在服务器或虚拟主机上下文中声明 Map。您可以在这些作用域的RewriteRuleRewriteCond指令中使用 Map(一旦创建)。您只是不能在那些范围内“声明”它。

以下各节描述了可以使用的各种* MapType *,并给出了每个示例。

int:内部函数

当使用int的 MapType 时,MapSource 是可用的内部RewriteMap函数之一。模块作者可以通过向ap_register_rewrite_mapfunc API 注册来提供其他内部功能。默认情况下提供的功能是:

  • toupper :
    将密钥转换为全部大写。

  • tolower :
    将密钥转换为所有小写字母。

  • escape :
    将密钥中的特殊字符转换为十六进制编码。

  • unescape :
    将密钥中的十六进制编码转换回特殊字符。

要使用这些功能之一,请创建一个RewriteMap引用 int 函数,然后在RewriteRule中使用它:

将 URI 重定向为其自身的全小写版本

RewriteMap lc int:tolower
RewriteRule "(.*)" "${lc:$1}" [R]

Note

请注意,此处提供的示例仅用于说明目的,而不是建议。如果要使 URL 不区分大小写,请考虑改用mod_speling

txt:纯文本 Map

当使用txt的 MapType 时,MapSource 是纯文本 Map 文件的文件系统路径,每行包含一个以空格分隔的键/值对。可选地,一行可以包含以“#”字符开头的 Comments。

有效的文本重写 Map 文件将具有以下语法:

# Comment line MatchingKey SubstValue MatchingKey SubstValue # comment

调用RewriteMap时,在一行的第一个参数中查找该参数,如果找到,则返回替换值。

例如,我们可以使用以下文件,使用 Map 文件将产品名称转换为易于识别的 URL 的产品 ID:

产品到 ID 的配置

RewriteMap product2id "txt:/etc/apache2/productmap.txt"
RewriteRule "^/product/(.*)" "/prods.php?id=${product2id:$1|NOTFOUND}" [PT]

我们在这里假设prods.php脚本在查找 Map 中找不到产品时收到id=NOTFOUND参数时知道该做什么。

然后,文件/etc/apache2/productmap.txt包含以下内容:

产品到 ID 的 Map

## ## productmap.txt - Product to ID map file ## television 993 stereo 198 fishingrod 043 basketball 418 telephone 328

因此,当请求http://example.com/product/television时,将应用RewriteRule,并且该请求在内部 Map 到/prods.php?id=993

Note: .htaccess files

给出的示例旨在用于服务器或虚拟主机范围。如果您打算在.htaccess文件中使用它,则需要从重写模式中删除前导斜杠以使其与任何内容匹配:

RewriteRule "^product/(.*)" "/prods.php?id=${product2id:$1|NOTFOUND}" [PT]

Cached lookups

查找到的密钥由 httpd 缓存,直到 Map 文件的mtime(修改时间)改变,或者重新启动 httpd 服务器。这样可以确保在许多请求调用的 Map 上具有更好的性能。

rnd:随机纯文本

当使用rnd的 MapType 时,MapSource 是纯文本 Map 文件的文件系统路径,该文件的每一行都包含一个键以及一个或多个由|分隔的值。如果密钥匹配,将随机选择这些值之一。

例如,您可以使用以下 Map 文件和指令通过反向代理在多个后端服务器之间提供随机负载平衡。图像发送到“静态”池中的一台服务器,而其他所有内容都发送到“动态”池中的一台。

RewriteMap 文件

## ## map.txt -- rewriting map ## static www1|www2|www3|www4 dynamic www5|www6

Configuration directives

RewriteMap servers "rnd:/path/to/file/map.txt"

RewriteRule "^/(.*\.(png|gif|jpg))" "http://${servers:static}/$1"  [NC,P,L]
RewriteRule "^/(.*)"                "http://${servers:dynamic}/$1" [P,L]

因此,当请求图像并且其中第一个规则匹配时,RewriteMap在 Map 文件中查找字符串static,该字符串会随机返回指定的主机名之一,然后将其用于RewriteRule目标。

如果您希望选择其中一台服务器的可能性更高(例如,如果其中一台服务器比其他服务器具有更多的内存,因此可以处理更多请求),只需在 Map 文件中多次列出它即可。

static www1|www1|www2|www3|www4

dbm:DBM 哈希文件

当使用dbm的 MapType 时,MapSource 是指向 DBM 数据库文件的文件系统路径,该文件包含要在 Map 中使用的键/值对。这与txtMap 完全相同,但是速度更快,因为对 DBM 进行了索引,而对文本文件则未进行索引。这样可以更快速地访问所需的密钥。

您可以选择指定特定的 dbm 类型:

RewriteMap examplemap "dbm=sdbm:/etc/apache/mapfile.dbm"

类型可以是sdbmgdbmndbmdb。但是,建议您仅使用 Apache HTTP Server 随附的httxt2dbmUtil,因为它将使用正确的 DBM 库,该库与构建 httpd 本身时使用的库相匹配。

要创建 dbm 文件,请首先按照txt部分中的说明创建文本 Map 文件。然后运行httxt2dbm

$ httxt2dbm -i mapfile.txt -o mapfile.map

然后,您可以在RewriteMap指令中引用结果文件:

RewriteMap mapname "dbm:/etc/apache/mapfile.map"

Note

请注意,对于某些 dbm 类型,将生成多个文件,并且具有通用的基本名称。例如,您可能有两个名为mapfile.map.dirmapfile.map.pag的文件。这是正常现象,您只需要在RewriteMap指令中使用基本名称mapfile.map

Cached lookups

查找到的密钥由 httpd 缓存,直到 Map 文件的mtime(修改时间)改变,或者重新启动 httpd 服务器。这样可以确保在许多请求调用的 Map 上具有更好的性能。

prg:外部重写程序

当使用prg的 MapType 时,MapSource 是可执行程序的文件系统路径,该程序将提供 Map 行为。这可以是编译的二进制文件,也可以是解释语言(例如 Perl 或 Python)的程序。

启动 Apache HTTP Server 时,该程序将启动一次,然后通过STDINSTDOUT与重写引擎进行通信。也就是说,对于每个 Map 函数查找,它都希望通过STDIN提供一个参数,并且应该在STDOUT上返回一个换行终止的响应字符串。如果没有相应的查找值,则 Map 程序应返回四个字符的字符串“ NULL”以指示这一点。

如果外部重写程序是在未将RewriteEngine设置为on的上下文中定义的,则不会启动它们。

默认情况下,外部重写程序以启动 httpd 的 user:group 运行。在 UNIX 系统上,可以通过将用户名和组名作为第三个参数以username:groupname格式传递给RewriteMap来更改此设置。

此功能利用了rewrite-map互斥锁,这是与程序进行可靠通信所必需的。互斥机制和锁定文件可以使用Mutex指令进行配置。

此处显示了一个简单的示例,该示例将用请求 URI 中的下划线替换所有破折号。

Rewrite configuration

RewriteMap d2u "prg:/www/bin/dash2under.pl" apache:apache
RewriteRule "-" "${d2u:%{REQUEST_URI}}"

dash2under.pl

#!/usr/bin/perl
$| = 1; # Turn off I/O buffering
while (<STDIN>) {
    s/-/_/g; # Replace dashes with underscores
    print $_;
}

Caution!

  • 使您的重写 Map 程序尽可能简单。如果程序挂起,它将导致 httpd 无限期地 await 来自 Map 的响应,从而导致 httpd 停止响应请求。

  • 确保关闭程序中的缓冲。在 Perl 中,这是通过示例脚本的第二行完成的:$| = 1;当然,其他语言也会有所不同。缓冲的 I/O 将使 httpd await 输出,因此它将挂起。

  • 请记住,只有在服务器启动时才启动该程序的一个副本。所有请求都需要克服这一瓶颈。如果许多请求必须经过此过程,或者脚本本身非常慢,则这可能会导致严重的速度下降。

dbd 或 fastdbd:SQL 查询

当使用dbdfastdbd的 MapType 时,MapSource 是一个 SQL SELECT 语句,它接受单个参数并返回单个值。

mod_dbd将需要配置为指向正确的数据库才能执行此语句。

此 MapType 有两种形式。使用dbd的 MapType 会使查询与每个 Map 请求一起执行,而使用fastdbd则在内部缓存数据库查找。因此,虽然fastdbd效率更高(因此也更快),但是直到重新启动服务器后,它才会 continue 对数据库进行更改。

如果查询返回多个行,则使用结果集中的随机行。

Example

RewriteMap myquery "fastdbd:SELECT destination FROM rewrite WHERE source = %s"

Note

查询名称将作为 SQL 预准备语句的标签传递给数据库驱动程序,因此将需要遵循数据库所需的任何规则(例如区分大小写)。

Summary

RewriteMap指令可以多次出现。对于每个 Map 函数,请使用一个RewriteMap指令声明其重写 Map 文件。

虽然您无法在每个目录上下文(.htaccess文件或<Directory>块)中“声明”一个 Map,但可以在每个目录上下文中“使用”此 Map。