W7 Pro IIS 7.5 覆盖 PHP 的 Location: Header

5

我正在用PHP创建一个RESTful API,遇到了一个问题。当客户端向服务器发送数据时,服务器应该返回:

Status code 201 CREATED
Header Location with the location of the new object
Content-Type application/xml

<SomeXmlData></SomeXmlData>

以下是在我的电脑上产生问题的虚拟代码:

<?php
header("Location: http://google.no/",true,201);
header("Content-Type: application/xml;charset=iso-8859-1");

echo "<?xml version=\"1.0\" encoding=\"utf-8\" standalone=\"yes\"?>\n";
echo "<Jada></Jada>";
?>

HTTP结果是

HTTP/1.1 201 Created
Content-Type: text/html; charset=UTF-8
Location: http://google.no/
Server: Microsoft-IIS/7.5
X-Powered-By: PHP/5.4.5
X-Powered-By: ASP.NET
Date: Wed, 22 Aug 2012 13:52:57 GMT
Content-Length: 209

<head><title>Document Moved</title></head>
<body><h1>Object Moved</h1>This document may be found <a HREF="http://google.no/">here</a></body><?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Jada></Jada>

由于 Location Header 的存在,PHP 会自动添加一些 HTML 代码和 HTML 内容类型到响应中。

因此,我的 API 将无法与其客户端正常工作。

编辑: IIS 7.5 Windows 7 专业版。


2
为什么您的内容类型标头和XML声明在字符编码方面存在不一致? - Quentin
3
我认为这是您的服务器在起作用,而不是 PHP。 - Waleed Khan
2
X-Powered-By: PHP/5.4.5 X-Powered-By: ASP.NET。每次我看到与IIS相关的东西,我都想尖叫奔跑。 - Quentin
在我的本地服务器上尝试了Apache:发送“Location”标头不会产生自动HTML。但是,应该注意,如果您使用http://example.com/dir而不是http://example.com/dir/,Apache会抱怨并输出类似的HTML:IIS也可能会这样做。 - Waleed Khan
这只是虚拟代码...在这段代码中,字符编码和PHP标签的关闭并不是优先考虑的问题,只是为了展示问题。 - Jan Sverre
显示剩余2条评论
4个回答

5

很抱歉带来不好的消息,但请看这里:

防止IIS在Location头存在时更改响应

编辑:我从未找到答案 - 我最终切换到了Apache

而且似乎IIS长期以来一直在干涉头文件:

http://forums.iis.net/t/1158431.aspx

这是IIS FastCGI模块的一个错误。它将在Windows 7 RTM中得到修复。我们还在探索可能的方法,以便将此修复程序提供给IIS 7。

希望如果这些错误相关(我预计它们是),如果您现在使用FastCGI,那么下面的修复方法可能会起作用。否则,切换到PHP非FastCGI模块也可能起作用,并且可能比投入Apache更容易。

http://support.microsoft.com/kb/980363


谢谢。最后我放弃了,然后创建了一个修复问题的IIS模块。 - Jan Sverre
+1 个聪明的解决方法。(能否对微软进行踩?) - LSerni
问题仍然存在于IIS 8.5.9600.16384 / Windows 8.1中。 - Florian Winter

4

解决方案是创建一个IIS模块,在FastCGI完成后将标头“Custom-Location”重写为“Location”。

然后,FastCGI就不知道我们发送了一个Location标头,它也不会修改我的响应。

该模块:

string Location = context.Response.Headers["Custom-Location"] as string;
if (!string.IsNullOrEmpty(Location))
{
    context.Response.Headers.Remove("Custom-Location");
    context.Response.AddHeader("Location", Location);
}

PHP:

header("Custom-Location: http://google.no",true,201);
header("Content-Type: application/xml");
echo "<xml></xml>";

(仍然是虚拟代码,不是完全正确的代码:))

我想澄清一些事情。这完全是关于处理程序而不是模块。当IIS在内容中添加一些HTML时,我们遇到了相同的问题。另一个有趣但不明显的事情是,您仍然可以为部署在您的IIS上的PHP环境创建托管处理程序。这与创建任何其他用托管代码编写的HTTP处理程序相同。 - Victor Ponamarev
我不明白这里发生了什么。你能解释一下这个如何修复iis中的问题吗? - twoLeftFeet
问题在于当存在Location标头时,IIS处理程序会修改HTTP响应体。我们的解决方案是发送'Custom-Location',然后IIS处理程序将不会对此做任何操作。然后,我制作了一个IIS模块,将'Custom-Location'标头重写为'Location' - 这个逻辑位于处理程序逻辑之后,因此处理程序将不会修改响应体。 - Jan Sverre
这个回答已经四年了,我们不再使用IIS,所以我无法确定它是否仍然有效。 - Jan Sverre

2

我在IIS 8.5上使用WP REST API时遇到了类似的问题。以下是HTML代码:

<head>
    <title>Document Moved</title>
</head>
<body>
    <h1>Object Moved</h1>
    This document may be found 
    <a HREF="[url-from-location-header]">here</a>
</body>

在每个带有Location头和状态码201 Created的json返回开头添加了was addedContent-Type被改为text/html; charset=UTF-8

在php.ini文件中添加cgi.rfc2616_headers = 1会导致以下结果:

  • 干净的body(json),不带有添加的html标签
  • 正确的“Content-Type”
  • 缺少“Location”头信息
  • 状态更改为200 OK

我不认为这是解决方案,这更像是用另一个问题替换了原来的问题。幸运的是,在我的情况下,这个新问题比原来的问题小。


我在使用flask-restless api (python)在IIS上遇到了问题。您能否详细解释一下您是如何使用iis解决这个问题的? - twoLeftFeet
在我看来,强制使用rfc2616头部不是一个有效的解决方案,因为许多PHP应用程序依赖于Apache头部,例如WordPress。 - Max Favilli
你还记得在 Location 头中返回的是相对 URL (/a/b/c...) 还是绝对 URL (https://...) 吗? - Florian Winter

1
为了规避IIS的这种行为,您可以使用出站重写规则。以下内容将查找状态201,如果是,则消除所有内容行,直到包括结束body标记为止:
<outboundRules>
  <rule name="Remove injected 201 content" preCondition="Status 201">
    <match filterByTags="None" pattern="^(?:.*[\r\n]*)*.*&lt;/body>" />
    <action type="Rewrite" value="" />
  </rule>
  <preConditions>
   <preCondition name="Status 201" patternSyntax="Wildcard">
     <add input="{RESPONSE_STATUS}" pattern="201" ignoreCase="false" />
   </preCondition>
  </preConditions>
</outboundRules>

这条规则在服务器2012上的IIS 8中导致了w3wp崩溃(状态为201)。它不喜欢正则表达式模式。我针对相同的先决条件创建了两个单独的规则,一个针对头部(模式为“*.head>”),一个针对正文(模式为“*.body>”),这解决了问题。 - mendel

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接