【问题标题】:trouble with utf-8 chars & apache2 rewrite rulesUTF-8 字符和 apache2 重写规则的问题
【发布时间】:2011-02-24 08:43:17
【问题描述】:

我看到了validating utf-8 in htaccess rewrite rule 的帖子,我认为这很好,但我首先遇到了一个更根本的问题:

我需要扩展以处理查询字符串参数、目录名、文件名以及用于向用户显示等的 utf-8 字符。

我用 DefaultCharset utf-8 和我的 php 配置了我的 Apache,如果这很重要的话。我最初的重写规则过滤了除常规 A-Za-z 以及下划线和连字符之外的所有内容。它奏效了。其他任何东西都会给你一个 404 (这就是我想要的!)现在,但是似乎一切都匹配,包括我不想要的东西,但是,尽管它似乎匹配它不会进入查询字符串,除非它是一个常规的 A-Za-z_- 字符串。

我觉得这很混乱,因为规则说将匹配到的任何内容放入查询字符串中:

这是原来的规则:

RewriteRule ^/puzzle/([A-Za-z_-]+)$ /puzzle.php?g=$1 [NC]

这是修改后的规则:

RewriteRule ^/puzzle/(\w+)$ /puzzle.php?g=$1 [NC]

我进行了更改,因为在某处我读到 \w 与 A-Zetc 中的所有 alpha 字符匹配。只匹配那些没有口音和东西的。

我使用哪些规则似乎并不重要:会发生什么:

在应用程序中我有这个:

echo $_GET['g'];

如果我给它提供一个像http://mydomain.com/puzzle/USA 这样的网址,它会回显“美国”并且工作正常。
如果我给它提供一个像http://mydomain.com/puzzle/México 这样的网址,它不会对此做出任何回应,并警告我索引 g 没有定义,当然也没有获得墨西哥的资源。
如果我给它提供一个像http://mydomain.com/puzzle/fuzzle/buzzle/j.qle 这样的网址,它会做同样的事情。
最后一种情况应该是404!

无论我使用上述哪种规则,它都会这样做。我配置了一个重写日志

   RewriteLogLevel 5
   RewriteLog /opt/local/apache2/logs/puzzles.httpd.rewrite

但它是空的。

这是来自常规访问日志(它给出的状态为 200)

[26/May/2010:11:21:42 -0700] "GET /puzzle/M%C3%A9xico HTTP/1.1" 200 342
[26/May/2010:11:21:54 -0700] "GET /puzzle/M/l.foo HTTP/1.1" 200 342

我该怎么做才能让这些 $%#$@(*#@!!! 字符而不是斜线、点或其他非 alpha 字符进入我的程序,并且一旦出现,它会正确解码它们吗??? posix char 类工作得更好吗?我还需要配置什么吗?

【问题讨论】:

  • 如果我在 serverfault 上交叉发布此内容,会不会令人不悦?它可能是也可能不是服务器问题。我把它放在这里是因为通常人们在这里回答得更快。
  • 为了有资格获得赏金,解决方案必须是干净的,在 Mac Win 和 *ix 服务器上同样有​​效,并且实际解决了这个问题。同时,我正忙于涉及符号链接的解决方法,因为我在这方面浪费了太多时间。

标签: php apache mod-rewrite utf-8


【解决方案1】:

我建议您激活 MultiViews 并忘记 mod_rewrite。在相关的Directory/VirtualHost 部分中添加到您的 apache 配置:

Options +MultiViews
#should already be set to this, but it doesn't hurt:
AcceptPathInfo Default

不,只要客户端在其 Accept 标头中包含对应的 mime 类型,您始终可以省略扩展。

现在/puzzle/whatever 的请求将映射到/puzzle.php 并且$_SERVER['PATH_INFO'] 将填充/whatever


如果你想用 mod_rewrite 来做它也是可能的。 RewriteRule 的测试字符串未转义(%xx 部分转换为它们所代表的实际字节)。您可以使用%{REQUEST_URI}%{THE_REQUEST} 获取原始转义字符串(最后一个还包含HTTP 方法和版本)。

按照惯例,Web 浏览器在 URL 中使用 UTF-8 编码。这意味着“México”将被 urlencoded 为 M%C2%82xico,而不是 M%82xico,如果浏览器使用 ISO-8859-1,这是预期的。此外,[a-zA-Z] 将不匹配 é。但是,这应该有效:

RewriteCond %{REQUEST_URI} ^/puzzle/[^/]*$
RewriteRule ^/puzzle/(.*)$ /puzzle.php?q=$1 [B,L]

您需要 B 来转义反向引用,因为您在查询字符串中使用它,其中允许的字符集小于 URI 的其余部分。

您应该注意的是RewriteRule 不支持 unicode。 .* 以外的任何东西都可能给出(可能)不正确的结果。即使[^/] 也可能不起作用,因为/“字符”(读取:字节)可能是多字节字符序列的一部分。如果 RewriteRule 支持 Unicode,那么您使用 \w 的解决方案应该可以工作。

由于您不想匹配子目录,并且RewriteRule ^/puzzle/[^/]* 不是一个选项,因此该检查将推迟到使用(转义)%{REQUEST_URI}RewriteCond

【讨论】:

    【解决方案2】:

    开...

    重写规则 ^/puzzle/(\w+)$ /puzzle.php?g=$1 [NC]

    如果我错了,请有人纠正我,但这是否意味着获取请求子目录的请求只是绕过此规则?

    另外,解决此问题的一种懒惰方法是也将“%”字符分组。据我所知,您只能在任何 url 路径上使用 url 编码。其实看:http://www.blooberry.com/indexdot/html/topics/urlencoding.htm

    我确信有更高级和更好的方法可以做到这一点,但这应该可以解决您的直接问题。

    【讨论】:

    • 抱歉,我认为我提供的信息不准确。根据 Apache 文档 (httpd.apache.org/docs/2.2/mod/mod_rewrite.html),引擎在处理之前对 url 进行转义,然后转义重写的输出。尝试使用 B 标志在处理过程中保持非字母字符在反向引用中转义。这应该让您使用该模式... RewriteRule ^/puzzle/([a-zA-Z0-9_-]|%[a-fA-F0-9]{2})+$ [NC,B] 否则,您必须在仅涵盖不规则字母字符的范围内搜索“\xFF”值。
    • 从那时起,使用 PHP 翻译 ['g'] 或者如果您不喜欢该值,则直接返回 '404' 标头。
    • 呃!我的意思是'RewriteRule ^/puzzle/(([a-zA-Z0-9_-]|%[a-fA-F0-9]{2})+)$ /puzzle.php?g=$1 [NC,B ]' 所以整个重复组被捕获。也许我应该暂时离开电脑。
    • 谢谢。我仍然对大局感到完全困惑,但我确实看到了你的第二个重写规则试图做什么,所以我正在 Mac 本地主机上对其进行测试。当我尝试在 Linux 上做一些测试时,我遇到了各种各样的问题。
    • 它的作用相同。我应该喂它什么:mydomain.com/puzzle/Méxicomydomain.com/puzzle/M%E9xico 或其他什么?顺便说一句,两者都不起作用。
    【解决方案3】:

    这是对驱逐舰答案的回应,但太长了。

    我对 unicode 的 URL 编码感到失望,因为它很容易解码以显示。所以也许这就是基本问题。最终,我将在 php 中使用 url_encode 来执行此操作,但我想我会尝试在线测试来测试一下:我去了 http://www.opinionatedgeek.com/dotnet/tools/urlencode/Encode.aspx 并尝试对 México 进行编码,结果是 M%c3%a9xico。我去了您指定的站点并尝试了它,结果 M%E9xico 不同!是哪个???我想我必须接受 php 函数实际上给我的任何东西。但是这两个都有一个 9,这意味着我必须接受数字和 %。这就是我必须包括的全部内容吗?

    我希望请求真正的子目录的请求符合这个规则,如果这就是你绕过它的意思,我宁愿他们实际上在子目录中呈现静态页面。这就是为什么我真的想排除 / 我以为我做了。但似乎匹配 / 之后的任何内容,包括嵌套的子目录并转到 puzzle.php 文件。

    这是我尝试过的,但没有乐趣:我使用了这个规则: 重写规则 ^/puzzle/([A-Za-z0-9_%-]+)$ /puzzle.php?g=$1 [NC] 如您所见,我将 % 和 0-9 添加到组中。我需要逃避 % 还是什么?我读到只有 \ 需要在方括号内转义。我希望这就是你的意思。这些是您通过编码任何可能的 unicode 字符串获得的唯一附加字符吗?然后我通过了 2 个不同的 url 编码版本的墨西哥。对于 M%E9xico,我现在得到 404 和这条消息: 在此服务器上未找到请求的 URL /puzzle/México。对于 M%c3%a9xico,我现在在 404 上收到此消息:在此服务器上未找到请求的 URL /puzzle/México。对于不存在的子目录,它现在应该给出 404。所以现在只是重写规则不起作用。这就是进步。重写日志也开始在其中获取内容:这里有一些。我将谷歌如何阅读这些日志:

    kidd108d-mac3:logs tpdick$ cat puzzles.httpd.rewrite 
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (2) init rewrite engine with requested uri /puzzle/M?xico
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (3) applying pattern '^/puzzle/([A-Za-z0-9_%-]+)$' to uri '/puzzle/M?xico'
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (1) pass through /puzzle/M?xico
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (3) [perdir /Users/tpdick/Sites/puzzles/] add path info postfix: /Users/tpdick/Sites/puzzles/puzzle.php -> /Users/tpdick/Sites/puzzles/puzzle.php/M?xico
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (3) [perdir /Users/tpdick/Sites/puzzles/] strip per-dir prefix: /Users/tpdick/Sites/puzzles/puzzle.php/M?xico -> puzzle.php/M?xico
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (3) [perdir /Users/tpdick/Sites/puzzles/] applying pattern '^(.*)/GeoP-Test/puzzle/(.*)$' to uri 'puzzle.php/M?xico'
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (1) [perdir /Users/tpdick/Sites/puzzles/] pass through /Users/tpdick/Sites/puzzles/puzzle.php
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (3) [perdir /Users/tpdick/Sites/puzzles/] add path info postfix: /Users/tpdick/Sites/puzzles/puzzle.php -> /Users/tpdick/Sites/puzzles/puzzle.php/M?xico
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (3) [perdir /Users/tpdick/Sites/puzzles/] strip per-dir prefix: /Users/tpdick::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (2) init rewrite engine with requested uri /puzzle/México
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (3) applying pattern '^/puzzle/([A-Za-z0-9_%-]+)$' to uri '/puzzle/México'
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (1) pass through /puzzle/México
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (3) [perdir /Users/tpdick/Sites/puzzles/] add path info postfix: /Users/tpdick/Sites/puzzles/puzzle.php -> /Users/tpdick/Sites/puzzles/puzzle.php/México
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (3) [perdir /Users/tpdick/Sites/puzzles/] strip per-dir prefix: /Users/tpdick/Sites/puzzles/puzzle.php/México -> puzzle.php/México
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (3) [perdir /Users/tpdick/Sites/puzzles/] applying pattern '^(.*)/GeoP-Test/puzzle/(.*)$' to uri 'puzzle.php/México'
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#910858/subreq] (1) [perdir /Users/tpdick/Sites/puzzles/] pass through /Users/tpdick/Sites/puzzles/puzzle.php
    ::1 - - [26/May/2010:15:54:37 --0700] [puzzles.net/sid#886b00][rid#904858/initial] (3) [perdir /Users/tpdick/Sites/puzzles/] add path info postfix: /Users/tpdick/Sites/puzzles/puzzle.php -> /Users/tpdick/Sites/puzzles/puzzle.php/México
    

    现在呢??

    【讨论】:

    • 我没有提到这是一台 Mac,而且我听说 Mac 与 unicode 不一致。这几乎就像 MacOS 在说哦,我知道,它是 unicode,然后在将其提供给 apache 或其他东西之前对其进行重新编码。我要去一个 linux 机器上玩,我很幸运能接触到,看看我是否能得到更好的结果。
    【解决方案4】:

    此解决方案基于:http://www.dracos.co.uk/code/apache-rewrite-problem/

    试试这个重写规则:

    AddDefaultCharset UTF-8
    RewriteEngine On
    RewriteCond %{THE_REQUEST} /puzzle/([^?\ /]+)
    RewriteRule ^puzzle/(.*)$ puzzle.php/%1 [L]
    

    如何获取查询参数:

    <?php
    // Get query param
    $g = substr($_SERVER['PATH_INFO'], 1); 
    echo "<p>g: $g</p>";
    
    // Test if '/' is present in URL for 404's
    $g2 = substr($_SERVER['REQUEST_URI'], 8); 
    if (strpos($g2, '/') === false) {
        // do stuff
    } else {
        // Send 404 header here
        echo "<p>404</p>";
    }
    ?>
    

    使用此解决方案,您必须从 php 发送 404。

    【讨论】:

      【解决方案5】:

      使用 CodeIgniter 和 utf-8 越南语 (Tiếng Việt) 文件.htaccess:

      RewriteEngine on
      RewriteCond %{REQUEST_FILENAME} !-d
      RewriteCond %{REQUEST_FILENAME} !-f
      RewriteRule ^(.+[a-z,A-Z,0-9,ễ].+)$ index.php/$1 [L]
      

      当 url 有 'ễn' 然后错误 => RewriteRule 有 'ễ'

      【讨论】:

        【解决方案6】:

        测试它([^/]+) 它可能对你有用

        【讨论】:

        • 写几个字的同时配合代码sn-p回答是个好主意。但是你做得很棒而且走在正确的轨道上,继续努力,考虑到这是你的第一个答案,
        猜你喜欢
        • 2011-11-25
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-12
        • 1970-01-01
        • 2013-04-13
        • 1970-01-01
        相关资源
        最近更新 更多