我正在尝试编写一个简单的 REST API,并且目前正在尝试正确配置我的.htaccess
文件。
我想要实现的目标如下:
- 必须
RewriteRule
将请求定向到实际的 RestController - 捕获所有未明确提供的
RewriteRule
带有 FITTING 错误的 请求- 针对任何文件、子目录或子目录中的文件的请求,未经处理
RewriteRule
都应该得到答复403
- 针对任何不存在的文件或子目录的请求都应该得到答复
404
- 针对任何文件、子目录或子目录中的文件的请求,未经处理
我目前所取得的成就是:
- ✅ 为不存在的文件、子目录以及子目录中的文件提供服务
404
- ✅ 在没有适当的情况下提供现有文件和子目录
RewriteRule
403
- ❌ 将有效的 API 请求转发到正确的端点(在我添加最后一条
403
规则后它就停止工作了) - ❌
403
为子目录中的现有文件提供适当的服务RewriteRule
- 例如,当我尝试访问时domain.com/subdir/existingfile.pdf
,它实际上显示了我不想要的文件。
由于子目录可以动态创建,我希望处理其中的所有子目录和文件 403,而无需手动指定它们。
这是我的当前.htaccess
:
Options -Indexes
RewriteEngine on
RewriteRule ^ - [E=HTTP_AUTHORIZATION:%{HTTP:Authorization}]
# REST mappings
RewriteRule ^json$ RestController.php?key=json [L,nc,qsa]
RewriteRule ^file/(.*)$ RestController.php?key=file&id=$1 [L,nc,qsa]
RewriteRule ^actions$ RestController.php?key=actions&id=none [L,nc,qsa]
RewriteRule ^actions/(.*)$ RestController.php?key=actions&id=$1 [L,nc,qsa]
# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^ - [R=404,NC,L]
RewriteRule ^ - [R=403,NC,L]
答案1
因为RestController.php
是一个文件,而不是你的“映射”之一,所以最后一条规则将无条件地提供 403。
您需要为 设定例外RestController.php
和/或使用END
标志(Apache 2.4 中的新功能)而不是 来L
阻止对 mod_rewrite 指令的所有进一步处理。(该L
标志仅停止当前处理。在目录上下文,例如.htaccess
,重写的 URL 被传递到下一个处理阶段,然后在第二阶段处理最后一条规则。)
您也不需要明确的规则来处理 404,因为默认情况下会发生这种情况。只有当请求映射到文件(或目录)时,您才需要一条规则来处理 403。
例如,RestController.php
最后一个规则块中有一个例外:
:
# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule !^RestController\.php$ - [R=403]
# A 404 will naturally be served otherwise...
L
当使用带有标志的非 3xx 状态时,不需要该标志R
。
您不一定需要检查目录(第二个条件),除非您碰巧在该目录中有一个DirectoryIndex
文档(例如index.php
或index.html
)(但您为什么要这样做呢?)。当请求没有文档的目录时,mod_autoindex 会自动提供 403 DirectoryIndex
。
或者,或者也可以使用END
前面规则中的标志:
# REST mappings
RewriteRule ^json$ RestController.php?key=json [END,NC,QSA]
RewriteRule ^file/(.*) RestController.php?key=file&id=$1 [END,NC,QSA]
RewriteRule ^actions$ RestController.php?key=actions&id=none [END,NC,QSA]
RewriteRule ^actions/(.*) RestController.php?key=actions&id=$1 [END,NC,QSA]
# catch all non-matched requests and throw 404 or 403 accordingly
RewriteCond %{REQUEST_FILENAME} -f [OR]
RewriteCond %{REQUEST_FILENAME} -d
RewriteRule ^ - [R=403]
# A 404 will naturally be served otherwise...
NC
这里不需要该标志。还请注意,我删除了$
in,^file/(.*)$
因为它不是必需的(正则表达式默认是贪婪的)。
在旁边:
# REST mappings RewriteRule ^json$ RestController.php?key=json [END,NC,QSA] RewriteRule ^file/(.*) RestController.php?key=file&id=$1 [END,NC,QSA] RewriteRule ^actions$ RestController.php?key=actions&id=none [END,NC,QSA] RewriteRule ^actions/(.*) RestController.php?key=actions&id=$1 [END,NC,QSA]
如果您标准化端点,您的“映射”可以简化为一条规则,即始终是key
和id
参数,并且id
可以是空的(不是“无”),无论如何您都需要验证(根据您的规则,/actions
和/actions/
都是有效请求,但导致不同的目标)。然后您在 PHP 脚本中验证目标。
例如:
RewriteRule ^(json|file|actions)(?:/(.*))?$ RestController.php?key=$1&id=$2 [END,NC,QSA]
这确实意味着/json/<foo>
和/file
将被成功重写为RestController.php
(否则将被您的原始规则忽略),但这些请求无论如何都应该由您的 REST API 验证来处理。也许.*
真的应该是.+
。