“漂亮的链接”是一个经常被请求的主题,但它很少被完全解释。 mod_rewrite是制作“漂亮链接”的一种方法,但它很复杂,其语法非常简洁,难以理解,文档假定HTTP具有一定程度的熟练程度。有人能用简单的术语解释“漂亮的链接”是如何工作的,以及如何使用mod_rewrite来创建它们?
其他常见名称,别名,干净网址条款:RESTful网址,用户友好网址,SEO友好网址,Slugging,MVC网址(可能用词不当)
答案 0 :(得分:98)
要了解什么mod_rewrite,您首先需要了解Web服务器的工作原理。 Web服务器响应HTTP requests。最基本级别的HTTP请求如下所示:
GET /foo/bar.html HTTP/1.1
这是浏览器向Web服务器请求 URL /foo/bar.html
的简单请求。重要的是要强调它不会请求文件,它只请求一些任意的URL。请求也可能如下所示:
GET /foo/bar?baz=42 HTTP/1.1
这与URL的请求一样有效,而且显然与文件无关。
Web服务器是一个侦听端口的应用程序,接受来自该端口的HTTP请求并返回响应。 Web服务器完全可以以任何方式响应任何请求,以任何方式配置它以响应。此响应不是文件,而是 HTTP响应,它可能与任何磁盘上的物理文件有关,也可能没有。 Web服务器不一定是Apache,还有许多其他Web服务器,它们都只是持久运行并附加到响应HTTP请求的端口的程序。你可以自己写一个。本段旨在使您与URL直接等于文件的任何概念脱节,这对于理解非常重要。 :)
大多数Web服务器的默认配置是查找与硬盘上的URL匹配的文件。如果服务器的文档根设置为/var/www
,则可能会查看文件/var/www/foo/bar.html
是否存在并提供服务(如果是)。如果文件以“.php”结尾,它将调用PHP解释器,然后返回结果。所有这些关联都是完全可配置的;一个文件不必以“.php”结尾,以便Web服务器通过PHP解释器运行它,并且URL不必与磁盘上的任何特定文件匹配即可发生。
mod_rewrite是一种重写内部请求处理的方法。当Web服务器收到URL /foo/bar
的请求时,您可以将该URL重写为其他内容,然后Web服务器将在磁盘上查找与之匹配的文件。简单的例子:
RewriteEngine On
RewriteRule /foo/bar /foo/baz
当规则与“/ foo / bar”匹配时,此规则会显示,将其重写为“/ foo / baz”。然后将处理请求,就好像已请求/foo/baz
一样代替。这可以用于各种效果,例如:
RewriteRule (.*) $1.html
此规则匹配任何内容(.*
)和捕获它((..)
),然后重写它以附加“.html”。换句话说,如果/foo/bar
是请求的网址,则会像处理/foo/bar.html
一样处理。有关正则表达式匹配,捕获和替换的详细信息,请参阅http://regular-expressions.info。
另一个经常遇到的规则是:
RewriteRule (.*) index.php?url=$1
再次匹配任何内容并将其重写到文件index.php,并在url
查询参数中附加最初请求的URL。即,对于进入的任何和所有请求,执行文件index.php,该文件可以访问$_GET['url']
中的原始请求,因此它可以随意执行任何操作。
主要是将这些重写规则放入 Web服务器配置文件中。 Apache还允许您将它们放入文档根目录中的.htaccess
文件中(即.php文件旁边)。
* 如果主Apache配置文件允许;它是可选的,但通常是启用的。
mod_rewrite并没有神奇地使你的所有网址“漂亮”。这是一种常见的误解。如果您的网站中有此链接:
<a href="/my/ugly/link.php?is=not&very=pretty">
mod_rewrite没有什么可以做的那么漂亮。为了使它成为一个漂亮的链接,你必须:
将链接更改为漂亮的链接:
<a href="/my/pretty/link">
使用服务器上的mod_rewrite使用上述任何一种方法处理对URL /my/pretty/link
的请求。
(可以将mod_substitute
结合使用来转换传出的HTML网页及其包含的链接。虽然这比仅仅更新HTML资源更省力。)
你可以创建很多mod_rewrite和非常复杂的匹配规则,包括链接几个重写,将请求代理到完全不同的服务或机器,返回特定的HTTP状态代码作为响应,重定向请求等。它非常强大,可以如果你理解了基本的HTTP请求 - 响应机制,那就习惯了。 不会自动使您的链接变得漂亮。
有关所有可能的标记和选项,请参阅official documentation。
答案 1 :(得分:73)
为了扩展deceze's answer,我想提供一些其他mod_rewrite功能的示例和解释。
以下所有示例均假设您已在RewriteEngine On
文件中加入.htaccess
。
让我们举个例子:
RewriteRule ^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$ /blog/index.php?id=$1&title=$2 [NC,L,QSA]
该规则分为4个部分:
RewriteRule
- 启动重写规则^blog/([0-9]+)/([A-Za-z0-9-\+]+)/?$
- 这称为模式,但我只是将其称为规则的左侧 - 您想要重写的内容blog/index.php?id=$1&title=$2
- 称为替换或重写规则的右侧 - 您要重写的内容[NC,L,QSA]
是重写规则的标志,用逗号分隔,我稍后会详细解释上述重写可让您链接到/blog/1/foo/
之类的内容,实际上会加载/blog/index.php?id=1&title=foo
。
^
表示页面名称的开头 - 因此它会重写example.com/blog/...
但不会重写example.com/foo/blog/...
(…)
括号表示一个正则表达式,我们可以将其捕获为规则右侧的变量。在这个例子中:
([0-9]+)
- 匹配长度最少为1个字符且仅包含数字值(即0-9)的字符串。这可以通过规则右侧的$1
引用-
或+
(注意{{ 1}}使用反斜杠进行转义,因为没有转义,它将作为regex repetition character执行。这可以通过规则右侧的+
引用$2
表示前面的字符是可选的,因此在这种情况下,?
和/blog/1/foo/
都会重写到同一个地方/blog/1/foo
表示这是我们要匹配的字符串的结尾这些是在重写规则末尾的方括号中添加的选项,用于指定特定条件。同样,你可以在the documentation中阅读很多不同的标志,但我会看一些更常见的标志:
$
无案例标志意味着重写规则不区分大小写,因此对于上面的示例规则,这意味着NC
和/blog/1/foo/
(或其任何变体)都将匹配。< / p>
/BLOG/1/foo/
最后一个标志表示这是应该处理的最后一条规则。这意味着当且仅当此规则匹配时,才会在当前重写处理运行中评估其他规则。如果规则不匹配,则将照常按顺序尝试所有其他规则。如果您未设置L
标记,则以后所有规则都将应用于重写网址。
L
自Apache 2.4起,您还可以使用END
标志。与它匹配的规则将完全终止进一步的别名/重写处理。 ([END]
标志通常可以触发第二轮,例如重写到子目录或从子目录重写时。)
[L]
查询字符串append标志允许我们将额外的变量传递给指定的URL,该URL将被添加到原始的get参数中。对于我们的示例,这意味着QSA
之类的内容会加载/blog/1/foo/?comments=15
/blog/index.php?id=1&title=foo&comments=15
这个标志不是我在上面的例子中使用过的,但是我认为值得一提。这允许您指定http重定向,并可选择包含状态代码(例如R
)。例如,如果您想在/ myblog / to / blog /上执行301重定向,您只需编写如下规则:
R=301
Rewrite conditions使重写更加强大,允许您为更具体的情况指定重写。你可以在the documentation中了解很多条件,但我会谈谈几个常见的例子并解释它们:
RewriteRule ^/myblog/(*.)$ /blog/$1 [R=301,QSA,L]
这是一种非常常见的做法,它会在您的域前添加# if the host doesn't start with www. then add it and redirect
RewriteCond %{HTTP_HOST} !^www\.
RewriteRule ^ http://www.%{HTTP_HOST}%{REQUEST_URI} [L,R=301]
(如果已经存在),并执行301重定向。例如,加载www.
会将您重定向到http://example.com/blog/
http://www.example.com/blog/
这种情况稍微不那么常见,但如果文件名是服务器上存在的目录或文件,则这是一个不执行的规则的好例子。
# if it cant find the image, try find the image on another domain
RewriteCond %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule (.*)$ http://www.example.com/$1 [L]
只会对文件扩展名为jpg,jpeg,gif或png(不区分大小写)的文件执行重写。 %{REQUEST_URI} \.(jpg|jpeg|gif|png)$ [NC]
将检查当前服务器上是否存在该文件,如果不存在则执行重写%{REQUEST_FILENAME} !-f
将检查当前服务器上是否存在该文件,如果不存在则执行重写答案 2 :(得分:5)
可以在不使用RewriteRules的情况下实现许多基本的虚拟URL方案。 Apache允许在没有.php
扩展名和虚拟PATH_INFO
参数的情况下调用PHP脚本。
现在,AcceptPathInfo On
通常默认启用。这基本上允许.php
和其他资源URL携带虚拟参数:
http://example.com/script.php/virtual/path
现在这个/virtual/path
在PHP中显示为$_SERVER["PATH_INFO"]
,您可以根据需要处理任何额外的参数。
将Apache单独的输入路径段分配到$1
,$2
,$3
并将它们作为不同的$_GET
变量传递给PHP,这并不方便。它只是模仿了漂亮的网址&#34;配置工作量减少。
.php
扩展名最简单的选项还可以避开.php
&#34;文件扩展名&#34;在URL中启用:
Options +MultiViews
由于匹配的基本名称,这为article.php
上的HTTP请求选择了/article
。这与上述PATH_INFO功能一起使用效果很好。因此,您可以使用http://example.com/article/virtual/title
之类的网址。如果您的传统Web应用程序具有多个PHP调用点/脚本,那么这是有意义的。
请注意,MultiViews具有不同/更广泛的用途。它会导致非常小的性能损失,因为Apache总是查找具有匹配基本名称的其他文件。它实际上适用于Content-Negotiation,因此浏览器会在可用资源中获得最佳替代方案(例如article.en.php
,article.fr.php
,article.jp.mp4
)。
.php
脚本的SetType或SetHandler 对于其他文件方案,避免在URL中携带.php
个后缀的更直接的方法是configuring the PHP handler。最简单的选项是通过.htaccess
覆盖默认的MIME /处理程序类型:
DefaultType application/x-httpd-php
这样您就可以将article.php
脚本重命名为article
(不带扩展名),但仍将其作为PHP脚本处理。
现在这可能会带来一些安全性和性能影响,因为现在所有无扩展名文件都将通过PHP传输。因此,您也可以仅为单个文件设置此行为:
<Files article>
SetHandler application/x-httpd-php
# or SetType
</Files>
这在某种程度上取决于您的服务器设置和使用的PHP SAPI。常见的替代方案包括ForceType application/x-httpd-php
或AddHandler php5-script
。
再次注意,此类设置从一个
.htaccess
传播到子文件夹。您始终应该禁用脚本执行(SetHandler None
和Options -Exec
或php_flag engine off
等)以获取静态资源,上传/目录等。
在众多选项中,Apache提供了mod_alias
个功能 - 有时与mod_rewrite
的RewriteRules一样有效。请注意,大多数必须在<VirtualHost>
部分中设置,而不是在每个目录.htaccess
配置文件中设置。
ScriptAliasMatch
主要用于CGI脚本,但也适用于PHP。它允许regexp像任何RewriteRule
一样。事实上,它可能是配置全能前控制器的最强大的选择。
普通Alias
也有一些简单的重写方案。
甚至可以使用普通的ErrorDocument
指令让PHP脚本处理虚拟路径。请注意,这是一个麻烦的解决方法,但是,除了GET请求之外什么都禁止,并且根据定义泛滥error.log。
答案 3 :(得分:1)
一个关于 URL 重写的常见问题是这样的:
<块引用>我目前的网址如下所示:
我把它们做成这样:
通过在我的 .htaccess 文件中使用它:
RewriteRule my-blog/(\d+)--i-found-the-answer my-blog/entry.php?id=$1
但我希望它们看起来像这样:
如何更改我的 .htaccess 文件以使其正常工作?
简单的答案是你不能。
每当您在网络浏览器中输入 URL、点击链接或显示引用图像的页面等时,浏览器都会对特定 URL 发出请求。该请求最终到达网络服务器,网络服务器给出响应。
重写规则只是一个规则,它表示“当浏览器请求一个看起来像 X 的 URL 时,给它们相同的响应好像他们要求 Y”。
当我们制定处理“漂亮网址”的规则时,请求是漂亮网址,而响应是基于内部丑陋的网址。它不能反过来,因为我们在服务器上编写规则,服务器看到的只是浏览器发送的请求。
鉴于重写规则的作用的这个基本模型,假设您正在向人类发出指令。你可以说:
但如果请求中没有该信息,您的说明将没有任何意义:
阅读这些说明的人会说“对不起,我怎么知道正确的数字是多少?”
有时,您会看到相反的规则,例如:
RewriteRule my-blog/entry.php?id=(\d+) my-blog/$1--i-found-the-answer [R]
此规则确实匹配左侧的丑陋 URL,并在右侧生成漂亮的 URL。那么我们肯定可以在漂亮部分的开头不写 ID 吗?
RewriteRule my-blog/entry.php?id=(\d+) my-blog/i-found-the-answer [R]
重要的区别在于 [R]
标志,这意味着该规则实际上是一个重定向 - 而不是“提供来自此 URL 的响应”,它的意思是“告诉浏览器改为加载此 URL”。
您可以将此视为其中一封自动电子邮件回复,内容为“抱歉,Joe Bloggs 目前正在休假;请将您的消息发送给 Jane Smith。”同样,上面的重定向告诉浏览器“抱歉,http://example.com/my-blog/entry.php?id=42
没有内容;请改为请求 http://example.com/my-blog/42--i-found-the-answer
。
这个类比的重点是,如果实际上没有叫 Jane Smith 的人在那里工作,或者如果他们不知道如何回答 Joe Bloggs 通常会处理的问题,则上述消息将没有多大用处。同样,如果您告诉浏览器请求的 URL 实际上没有做任何有用的事情,则 重定向 也没有用。一旦浏览器跟随重定向,它就会发出一个新请求,而当服务器收到新请求时,它仍然不知道 ID 号是什么。
网络服务器只包含请求中的信息,但如何使用该信息取决于您。
例如,不是通过 ID 查找博客文章,您可以将其 URL 直接存储在数据库中,然后编写一些代码直接在 PHP、Python、node.js 等中进行匹配。或者您可以使用根据用户在浏览器中设置的语言或基于 cookie 等,相同的 URL 显示不同的内容。
但是你不能在一个 .htaccess 文件中写一行代码来创造奇迹。