【问题标题】:Perl HTTP Request POST fails with TeamCity REST API使用 TeamCity REST API 的 Perl HTTP 请求 POST 失败
【发布时间】:2012-12-14 01:48:12
【问题描述】:

我有一个 perl 脚本通过 REST API 备份我们的 TeamCity 服务器,如下所示:

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:Request 文档的内容,但由于某种原因它失败了,抱怨我没有指定文件名:

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

然而,当我查看 TeamCity 的后端 REST 日志时,完整的请求似乎完好无损,并且与上面传递的请求相同。

命令成功日志:

[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 请求的方法之间是否还有其他隐藏的差异可能导致失败?

更新:这是通过 Data::Dumper 打印时每个请求的结果

成功发布:

$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' );

【问题讨论】:

  • 这里没有加起来。您确定第一个日志条目确实对应于失败的命令吗?尝试在失败的命令中使用可识别的内容,例如不同的文件名,并查看它是否会产生您期望的日志条目。
  • 如果您在检查和仔细检查相同的内容时只能看到我从头上拔了多少头发... ;-) 日志输出仅显示请求 URI TeamCity 声称已收到...所以肯定有一些更细微的区别。
  • 澄清一下:您使用的是HTTP::Request::Common?
  • 是 - 使用 HTTP::Request::Common qw{ POST GET };

标签: perl http rest post teamcity


【解决方案1】:

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

GET parameters 通过 URL 传递,通常是通过将 ? 附加到 URL 后跟实际的键/值组。这些可通过某些环境变量提供给在服务器上运行的脚本。由脚本在& 处拆分变量,在= 上拆分键/值对并撤消转义。

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

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

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

在某些情况下,我在这里的措辞可能是错误的,但基本面应该还可以。

【讨论】:

  • 澄清一下:我是否误读了 TeamCity 文档 (confluence.jetbrains.net/display/TW/REST+API+Plugin),还是应该向他们提出错误? :-)
  • 根据他们的示例,他们确实将POST HTTP 方法与GET parameters 样式参数传递用于备份方法调用。这是完全正确的,请注意,您确实误读了文档,或者您认为他们的框架将处理 GET parametersPOST data 相同(但它没有 - 其他框架可以)或者您根本不是了解两种传递参数方式的区别。
  • 另外:您必须在 Perl 中使用 POST() Perl 方法,但要对 URL 中的参数进行编码,就像您在已经为您工作的案例中所做的那样。
  • 我正在做一个假设,已经成功编写了 Perl 脚本来访问其他 REST API(例如 Nexus 和 JIRA)......老实说,我也忘记了两种不同风格的重要性传递参数。感谢您澄清它:-)
  • 不客气。许多框架没有区分参数的传递方式,并提供相同的 API 来访问它们;所以我完全可以理解你忘记了。
【解决方案2】:
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 语法混用也有助于澄清问题。

干杯。

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2015-06-11
  • 1970-01-01
  • 1970-01-01
  • 2020-08-27
  • 1970-01-01
  • 2016-07-12
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多