2012-12-13 81 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