为啥当我使用 Perl 的 REST::Client 发送 POST 请求,而不是使用 Perl 的 LWP::UserAgent 或 Python 时,我得到“405: Method Not All

Posted

技术标签:

【中文标题】为啥当我使用 Perl 的 REST::Client 发送 POST 请求,而不是使用 Perl 的 LWP::UserAgent 或 Python 时,我得到“405: Method Not Allowed”?【英文标题】:Why do I get "405: Method Not Allowed" when I send a POST request with Perl's REST::Client, but not with Perl's LWP::UserAgent or Python?为什么当我使用 Perl 的 REST::Client 发送 POST 请求,而不是使用 Perl 的 LWP::UserAgent 或 Python 时,我得到“405: Method Not Allowed”? 【发布时间】:2016-06-12 18:30:56 【问题描述】:

当我使用 Perl 的 REST::Client 发送 POST 请求时,我得到以下响应:

405: Method Not Allowed

但是,当我使用 Perl 的 LWP::UserAgent 或 Python 的 Requests 向同一个 URL 发送 POST 请求时,我得到一个“成功”响应。 GET 请求适用于 REST::Client 和 Python。

我该如何解决这个问题?


这是使用REST::Client 的代码(返回 HTTP 405):

use REST::Client;
use JSON;
$header = 
    'Auth' => '04211b77df',
    'Accept' => 'application/json',
    'Content-type' => 'application/json'
;

%body = (
    'sid' => '001',
    'pid' => 'c7b3d83',
    'file' => [
        
            'name' => 'file1.txt',
            'location' => 'folderA'
        ,
        
            'name' => 'file2.txt',
            'location' => 'folderB'
        
    ]
);

$client = REST::Client->new();
$client->POST('URL', encode_json(\%body), $header);

这是使用LWP::UserAgent(返回HTTP 200)的代码:

#!/use/bin/perl -w
use strict;
use LWP::UserAgent;
use Data::Dumper;
use JSON;

my $ua = LWP::UserAgent->new; 
my $req = HTTP::Request->new(POST => 'URL');

$req->header('Auth' => '04211b77df');
$req->header('Content-type' => 'application/json');
$req->header('Accept' => 'application/json');

my %body = (
    'sid' => '001',
    'pid' => 'c7b3d83',
    'file' => [
        
            'name' => 'file1.txt',
            'location' => 'folderA'
        ,
        
            'name' => 'file2.txt',
            'location' => 'folderB'
        
    ]
);
$req->content(encode_json(\%body));
$ua->request($req);

这是 Python 代码(返回 HTTP 200):

import requests, json, sys
headers = 
    'Auth': '04211b77df'
    'Accept': 'application/json'
    'Content-type': 'application/json'

data=
    'sid': '001',
    'pid': 'c7b3d83',
    'file': [
        
            'name': 'file1.txt',
            'location': 'folderA'
        ,
        
            'name': 'file2.txt',
            'location': 'folderB'
        
    ]


requests.request('POST', 'URL', data=data, headers=headers)

【问题讨论】:

如果您希望收到任何有用的答案,您需要edit 您的问题以包含minimal reproducible example。 如果您能够从一个客户端获得成功的结果,而在另一个客户端失败,我会查看客户端,而不是服务器。启动wireshark并观察来自两个客户端的流量,并寻找差异。 附带说明,您应该在编写的每个 Perl 脚本中使用use strict; use warnings 'all'; @PaulMcGuire 谢谢你会尝试一下 好的,我认为我看到了这个问题。 (REST::Client 实际上在幕后使用 LWP::UserAgent,因此您的最新编辑有助于缩小范围。) REST::Client 调用 $ua->simple_request 而不是 $ua->request:“与 request() 的区别是simple_request() 不会尝试处理重定向或身份验证响应。”我猜身份验证正在阻碍。我认为这是 REST::Client 中的一个错误。投票重新提出您的问题。 【参考方案1】:

您的 REST::Client 代码和您的 LWP::UserAgent 代码生成几乎相同的请求。* 但是,在后台使用 LWP::UserAgent 的 REST::Client 使用 $ua->simple_request() 发送请求,而您的 LWP ::UserAgent 代码使用$ua->request()

根据documentation:

request() 的区别在于simple_request() 不会尝试处理重定向或身份验证响应。

我猜是身份验证导致了这个问题。我还猜测您使用的 API 不需要对 GET 请求进行身份验证,因为您说这些正在使用 REST::Client。


要修复,请在构造函数中将follow 选项设置为1

my $client = REST::Client->new(follow => 1);

或调用setFollow方法:

my $client = REST::Client->new();
$client->setFollow(1);

这将正确处理身份验证响应,但它也将遵循重定向。不幸的是,您不能单独设置重定向和身份验证的行为,即long-standing bug。


* 唯一的区别是 REST::Client 添加了一个Content-length 标头,我认为这并不重要。

发送前转储 REST::Client 请求:

$VAR1 = bless(  
                 '_content' => '"sid":"001","pid":"c7b3d83","file":["location":"folderA","name":"file1.txt","location":"folderB","name":"file2.txt"]',
                 '_uri' => bless( do\(my $o = 'http://www.example.com'), 'URI::http' ),
                 '_headers' => bless(  
                                        'auth' => '04211b77df',
                                        'content-type' => 'application/json',
                                        'accept' => 'application/json',
                                        'content-length' => 122,
                                        '::std_case' =>  
                                                          'auth' => 'Auth'
                                                        
                                      , 'HTTP::Headers' ),
                 '_method' => 'POST'
               , 'HTTP::Request' );

发送前转储 LWP::UserAgent 请求:

$VAR1 = bless(  
                 '_content' => '"sid":"001","pid":"c7b3d83","file":["location":"folderA","name":"file1.txt","location":"folderB","name":"file2.txt"]',
                 '_uri' => bless( do\(my $o = 'http://www.example.com'), 'URI::http' ),
                 '_headers' => bless(  
                                        'auth' => '04211b77df',
                                        'content-type' => 'application/json',
                                        'accept' => 'application/json',
                                        '::std_case' =>  
                                                          'auth' => 'Auth'
                                                        
                                      , 'HTTP::Headers' ),
                 '_method' => 'POST'
               , 'HTTP::Request' );

实际发送请求时还会有其他细微差别,例如用户代理字符串,但我认为它们与您的问题无关。

【讨论】:

以上是关于为啥当我使用 Perl 的 REST::Client 发送 POST 请求,而不是使用 Perl 的 LWP::UserAgent 或 Python 时,我得到“405: Method Not All的主要内容,如果未能解决你的问题,请参考以下文章

为啥 STDIN 会导致我的 Perl 程序冻结?

为啥 ncat -exec 可以与 shell 脚本一起使用,但不能与 perl 脚本一起使用?

为啥 Perl 的两个 arg open 似乎去掉了换行符?

为啥这个 ISQL 命令不能通过 Perl 的 DBI 运行?

为啥我的 Perl 映射不返回任何内容?

如果我的代码中没有诊断信息,为啥 Perl 会编译 diagnostics.pm?