2012-12-13 56 views
0

我有一个Perl脚本通过REST API备份我们的TeamCity服务器,如下所示:Perl的HTTP POST请求失败,TeamCity的REST API

use strict; 
use LWP::UserAgent; 
use HTTP::Request::Common qw{ POST GET } 

# ... code ommitted for brevity ... # 

my $url = 'http://teamcity:8080/httpAuth/app/rest/server/backup'; 
my $req = POST($url . '?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=' . $filename); 
$req->authorization_basic($username, $password); 
my $resp = $ua->request($req);  

我试图与文档发布内容更加符合HTTP:请求,但由于某种原因失败了,抱怨我没有指定一个文件名:

# This fails 
my $req= POST($url, [ 'includeConfigs' => 'true', 
          'includeDatabase' => 'true', 
          'includeBuildLogs' => 'true', 
          'fileName'   => $filename, 
          ]); 

然而,当我看到在后端REST日志TeamCity的,完整的请求似乎已经使它完好无损,与上面通过的一样。

登录成功命令:

[2012-12-13 15:02:38,574] DEBUG [www-perl/5.805 ] - rver.server.rest.APIController - REST API request received: POST '/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', from client 10.126.31.219, authenticated as jsmith 

登录失败的命令:

[2012-12-13 14:57:00,649] DEBUG [www-perl/5.805 ] - rver.server.rest.APIController - REST API request received: POST '/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', from client 10.126.31.219, authenticated as jsmith 

有没有做一个POST请求,可能是导致故障的两种方法之间的任何其他隐藏的区别?

UPDATE:下面是每个请求的结果通过数据::自卸车打印时

成功POST:

$VAR1 = bless({ 
      '_content' => '', 
      '_uri' => bless(do{\(my $o = 'http://teamcity:8080/httpAuth/app/rest/server/backup?includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo')}, 'URI::http'), 
      '_headers' => bless({ 
            'content-type' => 'application/x-www-form-urlencoded', 
            'content-length' => 0, 
            'authorization' => 'Basic c3lzQnVpbGRTeXN0ZW1JOnBhaWQuZmFpdGg=' 
            }, 'HTTP::Headers'), 
      '_method' => 'POST' 
      }, 'HTTP::Request'); 

不成功POST:

$VAR1 = bless({ 
      '_content' => 'includeConfigs=true&includeDatabase=true&includeBuildLogs=true&fileName=foo', 
      '_uri' => bless(do{\(my $o = 'http://teamcity:8080/httpAuth/app/rest/server/backup')}, 'URI::http'), 
      '_headers' => bless({ 
            'content-type' => 'application/x-www-form-urlencoded', 
            'content-length' => 75, 
            'authorization' => 'Basic c3lzQnVpbGRTeXN0ZW1JOnBhaWQuZmFpdGg=' 
            }, 'HTTP::Headers'), 
      '_method' => 'POST' 
      }, 'HTTP::Request'); 
+0

这里没有东西加起来。你确定第一个日志条目真的对应于失败的命令吗?尝试在失败的命令中使用可识别的内容,如不同的文件名,并查看它是否生成您期望的日志条目。 – dan1111

+1

如果你只能看到有多少头发已经从我的脑海中抽出来,并且检查和重复检查相同... ;-)日志输出仅显示TeamCity声明已收到的请求URI ......所以必须有一些更细微的差异。 – RCross

+0

只是为了澄清:你使用'HTTP :: Request :: Common'? – dan1111

回答

2

我想你的服务器端脚本只能处理URL中编码的GET参数,而不是POST通过标准输入传输的数据。请注意,HTTP描述了几种不同的方法,这些方法是GET,POST,HEAD,DELETE等。然后有两种方法将数据传递到服务器上的应用程序。大多数情况下,其中一种方法也称为GET parameters,另一种称为POST data,因为GET parameters通常与HTTP GET方法一起使用,而POST data通常用于HTTP POST方法。但是,他们不需要。我认为你在成功案例中将POST方法与GET parameters混合在一起,在POST data中混淆了不成功的案例。

GET parameters通过URL传递,最常见的方法是将?附加到URL后面跟着实际的键/值pais。这些通过特定的环境变量可用于服务器上运行的脚本。这取决于脚本在&上拆分变量,拆分=上的键/值对并撤销转义。

对于POST data,环境变量CONTENT_LENGTH通知脚本从其标准输入中读取多少个字节。实际的键/值对通过不同的编码传送,通常作为多部分编码的内容。是的,POST HTTP请求(大部分来自HTML <form>)也可以发送URL编码,如GET parameters,但HTTP标准对URL包括所有参数都施加了长度限制。因此,通过标准输入而不是通过URL传输数据的方法。

现在看起来您的服务器端脚本可以评估URL编码的参数(又名GET parameters)参数,但不包括通过标准输入发布给它的数据。即使您使用HTTP方法/动词POST,但在您的成功案例中,您实际上并未通过标准输入传输值为POST data的值。在这种情况下,您可以简单地将POST(...)换成GET(...),它应该仍然有效。

在你不成功的情况下,你可以使用HTTP方法和POST data传输值。

我的言论在这里可能是错误的,但基本原则仍然可以。

+0

只是要清楚:我误解了TeamCity文档(http://confluence.jetbrains.net/display/TW/REST+API+Plugin),还是应该向他们提出错误? :-) – RCross

+0

根据他们的例子,它们实际上将'POST' HTTP方法与'GET parameters'风格参数混合传递给备份方法调用。这是完全有效的,请记住,你确实误读了文档,或者你认为他们的框架将'GET参数'与'POST数据'相同(但它不是 - 其他框架可以),或者你是根本没有意识到两种传递参数之间的差异。 –

+0

此外:您必须在Perl中使用'POST()'Perl方法,但是需要在URL中对参数进行编码,就像您已经在已经为您工作的情况下一样。 –

-1
my $url= my $url = 'http://teamcity:8080/httpAuth/app/rest/server/backup'; 
my $req= POST($url, { 'includeConfigs' => 'true', 
          'includeDatabase' => 'true', 
          'includeBuildLogs' => 'true', 
          'fileName'   => $filename, 
          }); 

注意' {}'(散列参考,而不是阵列参考)。另外,不要将查询字符串(GET)语法与POST语法混合起来,这对澄清问题很有帮助。

干杯。

+0

不幸的是,切换到“{}”没有任何区别。我按照http://search.cpan.org/~gaas/HTTP-Message-6.06/lib/HTTP/Request/Common.pm使用数组ref。 – RCross

+0

我以为同样的事情,但文档将它作为一个arrayref并且代码循环,就好像它是一个arrayref。 – gpojd