我有一个存储LWP :: UserAgent的对象。我想对使用该UA的不同呼叫使用不同的cookie jar,因此我决定在拨打电话时设置cookie_jar
local
。
以下代码显示了我没有调试内容(用于读取,未运行)的操作。下面是另一个版本,有很多调试输出。
package Foo;
use strictures;
use Moo;
use LWP::UserAgent;
has ua => (
is => 'ro',
default => sub { my $ua = LWP::UserAgent->new; $ua->cookie_jar( {} ); return $ua; },
);
sub request {
my ($self, $cookie_jar) = @_;
local $self->{ua}->{cookie_jar} = $cookie_jar;
$self->ua->get('http://www.stackoverflow.com');
}
package main;
my $foo = Foo->new;
my $new_jar = HTTP::Cookies->new;
$foo->request( $new_jar );
所以基本上我决定在本地覆盖cookie jar。不幸的是,当我们调用get
时,它仍将使用最初位于UA对象内的cookie jar。
package Foo;
use strictures;
use Moo;
use LWP::UserAgent;
use HTTP::Cookies;
use Data::Printer;
use feature 'say';
has ua => (
is => 'ro',
default => sub { my $ua = LWP::UserAgent->new; $ua->cookie_jar( {} ); return $ua; },
);
sub request {
my ($self, $cookie_jar) = @_;
say "before local " . $self->{ua}->{cookie_jar};
local $self->{ua}->{cookie_jar} = $cookie_jar;
$self->ua->get('http://www.stackoverflow.com');
print "local jar " . p $self->{ua}->{cookie_jar};
say "after local " . $self->{ua}->{cookie_jar};
}
package main;
use Data::Printer;
use HTTP::Cookies;
my $foo = Foo->new;
say "before outside of local " . $foo->{ua}->{cookie_jar};
my $new_jar = HTTP::Cookies->new;
say "before outside of local " . $new_jar;
$foo->request( $new_jar );
say "after outside of local " . $foo->{ua}->{cookie_jar};
print "global jar " . p $foo->ua->cookie_jar;
__END__
before outside of local HTTP::Cookies=HASH(0x30e1848)
before outside of local HTTP::Cookies=HASH(0x30e3b20)
before local HTTP::Cookies=HASH(0x30e1848)
local jar HTTP::Cookies {
public methods (13) : add_cookie_header, as_string, clear, clear_temporary_cookies, DESTROY, extract_cookies, load, new, revert, save, scan, set_cookie, set_cookie_ok
private methods (3) : _host, _normalize_path, _url_path
internals: {
COOKIES {}
}
}after local HTTP::Cookies=HASH(0x30e3b20)
after outside of local HTTP::Cookies=HASH(0x30e1848)
global jar HTTP::Cookies {
public methods (13) : add_cookie_header, as_string, clear, clear_temporary_cookies, DESTROY, extract_cookies, load, new, revert, save, scan, set_cookie, set_cookie_ok
private methods (3) : _host, _normalize_path, _url_path
internals: {
COOKIES {
stackoverflow.com {
/ {
prov [
[0] 0,
[1] "185e95c6-a7f4-419a-8802-42394776ef63",
[2] undef,
[3] 1,
[4] undef,
[5] 2682374400,
[6] undef,
[7] {
HttpOnly undef
}
]
}
}
}
}
}
正如您所看到的,HTTP :: Cookies对象已被本地化并正确替换。地址看起来完全正确。
但是p
的输出却讲述了一个不同的故事。 LWP :: UA根本没有使用local
cookie罐。这仍然是一个新鲜,空洞的。
如何使用local
代替?
我尝试过使用Moo,Moose和经典的bless
对象。所有都表现出这种行为。
编辑:由于评论中提到了这一点,让我再详细说明为什么我需要这样做。这将是一个有点咆哮。
TLDR:为什么我不想要替代解决方案,但要理解并解决问题
我正在构建一个基于Dancer2的webapp,它将与Plack和多个worker(Twiggy::Prefork一起运行 - 多个forks中的多个线程)。它将允许用户使用第三家公司的服务。该公司提供SOAP Web服务。将我的应用程序视为此服务的自定义前端。在Web服务上调用“登录用户”。它返回该特定用户的cookie(sessionid),我们需要在每次连续调用时传递该cookie。
要做SOAP-stuff我正在使用XML :: Compile :: WSDL11。编译这个东西非常昂贵,所以每次处理路由时我都不想这样做。这样效率低下。因此,当应用程序启动时,将从WSDL文件编译SOAP客户端。然后由所有工人共享。
如果共享客户端对象,则也会共享内部的用户代理。饼干罐也是如此。这意味着如果同时有两个请求,则sessionid可能会混淆。该应用最终可能会向用户发送错误的内容。
这就是我决定本地化cookie罐的原因。如果它是请求的本地唯一的,它将永远不能干扰并行发生的另一个工作者的请求。只为每个请求制作一个新的饼干罐不会削减它。它们仍然会被共享,甚至可能会丢失,因为在最坏的情况下它们会相互覆盖。
另一种方法是实现锁定机制,但这完全超出了拥有多个工作人员的目的。
我看到的唯一其他解决方案是使用另一个SOAP客户端。有SOAP :: WSDL,它不能在较新的Perls上运行。根据CPAN测试人员的说法,它打破了5.18并且我已经验证了这一点。它会更有效,因为它像代码生成器一样工作并预先创建比使用每次编译WSDL文件更便宜的类。但是,由于它被打破了,这是不可能的。
SOAP :: Lite将编译WSDL,并且非常糟糕。如果在我看来可以避免,那么任何人都不应该在生产中使用它。我看到的唯一替代方法是在不使用WSDL文件的情况下实现调用,并直接使用XML解析器解析结果,忽略模式。但这些都是大结果。这将非常不方便。
我对这个咆哮的结论是,我真的很想理解为什么Perl不想在这种情况下本地化cookie jar并修复它。
答案 0 :(得分:2)
也许您不是使用local
而是使用LWP::UserAgent
的clone
和cookie_jar
方法。
...
sub request {
my ($self, $new_cookie_jar) = @_;
my $ua = $self->ua; # cache user agent
if( defined $new_cookie_jar ){
# create a new user agent with the new cookie jar
$ua = $ua->clone;
$ua->cookie_jar( $new_cookie_jar );
}
my $result = $ua->get('http://www.stackoverflow.com');
# allow returning the newly cloned user agent
return ( $result, $ua ) if wantarray;
return $result;
}
如果你不想这样做,你至少应该使用这些方法,而不是操纵对象的内部。
...
sub request {
my ($self, $new_cookie_jar) = @_;
my $ua = $self->ua; # cache user agent
my $old_cookie_jar = $ua->cookie_jar( $new_cookie_jar );
my $result = $ua->get('http://www.stackoverflow.com');
# put the old cookie jar back in place
$ua->cookie_jar( $old_cookie_jar );
return $result;
}