Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl-Sensitive Sunglasses
 
PerlMonks  

Comment on

( #3333=superdoc: print w/replies, xml ) Need Help??

Hello fellow Monks,

I got again stack on something possibly (minor) problem that I can not over come and I need to ask you guys in case that someone know more about it.

My question is extension of How to pass credentials through REST::Client. This time I need to POST a file through the module REST::Client.

The scenario is more or less the same, I have a server listening on the background and I use a small module to send a file. Sample of code:

restClient.pl

#!/usr/bin/perl use strict; use warnings; use Data::Dumper; use MyClientRest::ClientRest; my $host = "http://127.0.0.1:8000"; # instatiate class my $object = new ClientRest( $host ); my $username = "user"; my $password = "password"; my $file = 'test.txt'; my $upload = '/upload/'; my %optionsFile = ( "url" => $upload, "file" => $file, "username" => $username, "password" => $password ); my $postFile = $object->postSnippetsFile( %optionsFile ); print Dumper $postFile; __END__ $ perl restClient.pl $VAR1 = 'Not a SCALAR reference at /usr/local/share/perl/5.22.1/LWP/Pr +otocol/http.pm line 260. ';

Code in the main module.

ClientRest.pm

package ClientRest; use JSON; use Carp; use strict; use warnings; use MIME::Base64; use Data::Dumper; use version; our $VERSION; $VERSION = qv('0.0.1'); use REST::Client; sub new { my $class = shift; my $self = { _host => shift, }; _parameterValidation($self); # instatiate Rest::Client and create constructor my $client = REST::Client->new({ host => $self->{_host}, timeout => 10, }); $self->{_client} = $client; bless $self, $class; return $self; } sub _parameterValidation { my( $self ) = @_; croak "Invalid host syntax: sample 'http://<host>:<port>' " unless ( $self->{_host} =~ /^http:\/\/\d{1,3}\.\d{1,3}\.\d{1,3}\.\ +d{1,3}\:\d{1,5}$/ ); } sub postSnippetsFile { my ( $self, %options ) = @_; my $headers = { "Content" => {'file' => $options{file}}, "Content-type" => 'multipart/form-data', "Authorization" => 'Basic ' . encode_base64($options{username} . ':' . $options{password +}) }; $self->{_client}->POST( $options{url}, $headers ); return $self->{_client}->responseContent(); } 1;

If I use curl POST and it works as expected. Sample bellow:

$ curl -u user:password -H "Content-Type: multipart/form-data" -H "Acc +ept: application/json" -H "Expect:" -F file=@test.txt -X POST http:// +127.0.0.1:8000/upload/ | jq '.' % Total % Received % Xferd Average Speed Time Time Time + Current Dload Upload Total Spent Left + Speed 100 455 100 249 100 206 2547 2107 --:--:-- --:--:-- --:--: +-- 2567 { "url": "http://127.0.0.1:8000/snippets/2/", "id": 2, "highlight": "http://127.0.0.1:8000/snippets/2/highlight/", "owner": "user", "title": "Default Title", "code": "Test POST file Perl", "linenos": false, "language": "perl", "style": "emacs" }

I was reading online on how to post a file with this module and I found How to POST attachment to Confluence page using REST API with perl?, HTTP::Request::Common and multipart/form-data, PUT a Multipart request in PERL and POST a Multipart request in Perl via LWP etc etc. All links point to the same solution as the one that I have implemented.

From the error that I am getting:

$ perl restClient.pl $VAR1 = 'Not a SCALAR reference at /usr/local/share/perl/5.22.1/LWP/Pr +otocol/http.pm line 260. ';

I checked the module following snippet is from line (257-274) where the error is coming:

else { # Set (or override) Content-Length header my $clen = $request_headers->header('Content-Length'); if (defined($$content_ref) && length($$content_ref)) { $has_content = length($$content_ref); if (!defined($clen) || $clen ne $has_content) { if (defined $clen) { warn "Content-Length header value was wrong, fixed"; hlist_remove(\@h, 'Content-Length'); } push(@h, 'Content-Length' => $has_content); } } elsif ($clen) { warn "Content-Length set when there is no content, fixed"; hlist_remove(\@h, 'Content-Length'); } }

I can understand that the header is producing the problem but I am not sure where I am going wrong. Can anyone else spot the problem?

Update: I tried also to define in the file in the header as HoA (Hash Of Arrays) e.g.:

my $headers = { "Content" => ['file' => [$options{file}]], "Content-type" => 'multipart/form-data', "Authorization" => 'Basic ' . encode_base64($options{username} . ':' . $options{password +}) };

Minor note also here, normal post with dictionary (data) it works as expected. Sample of post (important parts of code):

# module part sub postSnippets { my ( $self, %options ) = @_; my $headers = { "Content-type" => 'application/json; charset=UTF-8 +', "Authorization" => 'Basic ' . encode_base64($options{username} . ':' . $options{password +}) }; $self->{_client}->POST( $options{url}, encode_json($options{hashRef}), $headers ); return decode_json $self->{_client}->responseContent(); } # script part my $hashRef = { "title" => "Test Title", "code" => "print \"Test POST Request\"", "linenos" => "false", "language" => "perl", "style" => "emacs" }; my %options = ( "url" => $url, "hashRef" => $hashRef, "username" => $username, "password" => $password ); my $post = $object->postSnippets( %options ); print Dumper $post; # output $ perl restClient.pl $VAR1 = { 'linenos' => bless( do{\(my $o = 0)}, 'JSON::PP::Boolean' ), 'id' => 2, 'language' => 'perl', 'code' => 'print "Test POST Request"', 'title' => 'Test Title', 'style' => 'emacs', 'highlight' => 'http://127.0.0.1:8000/snippets/2/highlight/' +, 'url' => 'http://127.0.0.1:8000/snippets/2/', 'owner' => 'user' };

The models that process the file upload and the simple code post are different but they should work with the a bit different header as the sample of code posted above.

Update2: After a lot of experimentation and research on the web I found that I a missing the body, I have added the body but now something else is missing. I get the following error:

$VAR1 = '{"detail":"Multipart form parse error - Invalid boundary in m +ultipart: None"}';

Sample of code with the body:

sub postSnippetsFile { my ( $self, %options ) = @_; my $headers = { "Content-type" => 'multipart/form-data', "Content" => [ file => [$options{file}]], "Authorization" => 'Basic '. encode_base64($options{username} . ':' . $options{password +}), }; $self->{_client}->POST( $options{url}, encode_json({file => $options{file}}), $headers ); return $self->{_client}->responseContent(); }

Update3: Solution provided further down with sample of code. Hope this helps someone else also in future.

Thank you in advance for everyone time and effort.

Seeking for Perl wisdom...on the process of learning...not there...yet!

In reply to POST file through REST::Client [SOLVED] by thanos1983

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post; it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.
  • Log In?
    Username:
    Password:

    What's my password?
    Create A New User
    Chatterbox?
    and all is quiet...

    How do I use this? | Other CB clients
    Other Users?
    Others making s'mores by the fire in the courtyard of the Monastery: (3)
    As of 2018-04-24 21:29 GMT
    Sections?
    Information?
    Find Nodes?
    Leftovers?
      Voting Booth?
      Notices?