How can I get the entire request body with CGI.pm?

2020-02-10 12:43发布

问题:

I'm trying to write a Perl CGI script to handle XML-RPC requests, in which an XML document is sent as the body of an HTTP POST request.

The CGI.pm module does a great job at extracting named params from an HTTP request, but I can't figure out how to make it give me the entire HTTP request body (i.e. the XML document in the XML-RPC request I'm handling).

If not CGI.pm, is there another module that would be able to parse this information out of the request? I'd prefer not to have to extract this information "by hand" from the environment variables. Thanks for any help.

回答1:

You can get the raw POST data by using the special parameter name POSTDATA.

my $q = CGI->new;
my $xml = $q->param( 'POSTDATA' );

Alternatively, you could read STDIN directly instead of using CGI.pm, but then you lose all the other useful stuff that CGI.pm does.

The POSTDATA trick is documented in the excellent CGI.pm docs here.



回答2:

Right, one could use POSTDATA, but that only works if the request Content-Type has not been set to 'multipart/form-data'.

If it is set to 'multipart/form-data', CGI.pm does its own content processing and POSTDATA is not initialized.

So, other options include $cgi->query_string and/or $cgi->Dump.

The $cgi->query_string returns the contents of the POST in a GET format (param=value&...), and there doesn't seem to be a way to simply get the contents of the POST STDIN as they were passed in by the client.

So to get the actual content of the standard input of a POST request, if modifying CGI.pm is an option for you, you could modify around line 620 to save the content of @lines somewhere in a variable, such as:

$self->{standard_input} = join '', @lines;

And then access it through $cgi->{standard_input}.



回答3:

To handle all cases, including those when Content-Type is multipart/form-data, read (and put back) the raw data, before CGI does.

use strict;
use warnings;

use IO::Handle;
use IO::Scalar;

STDIN->blocking(1); # ensure to read everything
my $cgi_raw = '';

{ 
  local $/; 
  $cgi_raw = <STDIN>;
  my $s;
  tie  *STDIN, 'IO::Scalar', \$s;
  print STDIN $cgi_raw;
  tied(*STDIN)->setpos(0);
}

use CGI qw /:standard/;
...


标签: perl cgi