POST 使用 Perl 请求并在 dancer 路由中读取响应对象

POST request with Perl and read response object within dancer route

尝试用 perl 编写以下内容的确切等价物:

curl -H "Content-Type: application/json" -X POST -d '{"user": { "uid":"13453"},"access_token":"3428D3194353750548196BA3FD83E58474E26B8B9"}' https://platform.gethealth.io/v1/health/account/user/

没有使用 perl 的经验,这是我尝试过的:

use HTTP::Request::Common;
use LWP::UserAgent;

get '/gethealthadduser/:id' => sub {
  my $ua = LWP::UserAgent->new;
  $ua->request(POST 'https://platform.gethealth.io/v1/health/account/user', [{"user": { "uid":param("id")},"access_token":config->{gethealthtoken}}]);
};

我认为您已经在使用 Dancer,或者您正在向现有应用程序添加一些东西,目标是将 POST 请求隐藏到 API 后面的另一个服务。

在您的 curl 示例中,您有 Content-Type application/json,但在您的 Perl 代码中,您发送的是一个表单。这很可能是 Content-Type application/x-www-form-urlencoded。这可能不是服务器想要的。

除此之外,您将表单数据作为数组引用传递,这让 POST 相信它们是 header。那不是你想要的。

为了完成与 curl 相同的操作,您还需要几个步骤。

  • 您需要将数据转换为 JSON。幸运的是 Dancer 带来了 a nice DSL keyword to_json 可以轻松做到这一点。
  • 你需要告诉LWP::UserAgent使用右边的Content-Typeheader。那是 application/json,您可以在请求级别设置它,或者作为用户代理 object 的默认设置。我会做前者。
  • 除此之外,我建议不要使用 HTTP::Request::Common 将关键字导入 Dancer 应用程序。 GETPOST 等等是 upper-case 而 Dancer DSL 有 getpost 即 lower-case,但它仍然令人困惑。明确地使用 HTTP::Request 代替。

这是最后一件事。

use LWP::UserAgent;
use HTTP::Request;

get '/gethealthadduser/:id' => sub {
    my $ua  = LWP::UserAgent->new;
    my $req = HTTP::Request->new(
        POST => 'https://platform.gethealth.io/v1/health/account/user',
        [ 'Content-Type' => 'application/json' ],                                   # headers
        to_json { user => param("id"), access_token => config->{gethealthtoken} },  # content
    );

    my $res = $ua->request($req);

    # log the call with log level debug
    debug sprintf( 'Posted to gethealth.io with user %s and got a %s back.', 
        param('id'), 
        $res->status_line
    );

    # do things with $res
};

尝试使用 HTTP::Tiny(它在 CPAN 上)。恕我直言,它比 LWP::UserAgent 更简洁,尽管后者更受欢迎。

下面是一些开箱即用的代码:

use HTTP::Tiny 0.064;  # use a recent version or better

my $url = 'https://api.somewhere.com/api/users';
my $data = {
  first_name => "joe",
  last_name => "blow"
};
my $method = 'POST';


my $default_headers = {
  'Authorization' => "Bearer ".$token,  # if needed
  'Accept' => 'application/json'
};

my $tiny = HTTP::Tiny->new(
  agent => 'mywebsite.com',
  default_headers => $default_headers,
  timeout => 30
);

my $response;
if ( ($method eq 'POST') || ($method eq 'PUT') ) {
  $response = $tiny->request($method, $url, {
    headers => {
      'Content-Type' => 'application/json'
    },
    content => &toJSON($data)
  });
}
else {
  if ($data) {
    die "data cannot be included with method $method"; 
  }
  $response = $tiny->request($method, $url); 
}

die unless $response->{'success'};

祝你项目顺利!

以下是已发布参数格式和结构正确的解决方案:

get '/api/gethealthadduser/:id' => sub {
    my %user = (
        uid  => param("id")
    );
    # my $user = {
    #     uid  => param("id")
    # };

    my $ua  = LWP::UserAgent->new;
    my $req = HTTP::Request->new(
        POST => 'https://platform.gethealth.io/v1/health/account/user/',
        [ 'Content-Type' => 'application/json' ],                                   # headers
        JSON::to_json({ user => \%user, access_token => config->{gethealthtoken} })  # content
    );
    my $res = $ua->request($req);
    print STDERR Dumper($res);
    $res;
};