- NEW FEATURE: Catalyst::Response can now pull response from a PSGI
specification response. This makes it easier to host external Plack
applications under Catalyst. See Catalyst::Response->from_psgi_response
+ - NEW FEATURE: New configuration option 'use_hash_multivalue_in_request'
+ will populate $request methods 'parameters', 'body_parameters' and
+ 'query_parameters' with an instance of Hash::MultiValue instead of a
+ HashRef. This is used by Plack and is intended to reduce the need to
+ write defensive logic since you are never sure if an incoming parameter
+ is a scalar or arrayref.
5.90049_003 - 2013-09-20
- Documented the new body_data method added in the previous release
my %p = ( _log => $self->log );
$p{_uploadtmp} = $self->_uploadtmp if $self->_has_uploadtmp;
$p{data_handlers} = {$self->registered_data_handlers};
+ $p{_use_hash_multivalue} = $self->config->{use_hash_multivalue_in_request}
+ if $self->config->{use_hash_multivalue_in_request};
\%p;
}
=item *
+C<use_hash_multivalue_in_request>
+
+In L<Catalyst::Request> the methods C<query_parameters>, C<body_parametes>
+and C<parameters> return a hashref where values might be scalar or an arrayref
+depending on the incoming data. In many cases this can be undesirable as it
+leads one to writing defensive code like the following:
+
+ my ($val) = ref($c->req->parameters->{a}) ?
+ @{$c->req->parameters->{a}} :
+ $c->req->parameters->{a};
+
+Setting this configuration item to true will make L<Catalyst> populate the
+attributes underlying these methods with an instance of L<Hash::MultiValue>
+which is used by L<Plack::Request> and others to solve this very issue. You
+may prefer this behavior to the default, if so enable this option (be warned
+if you enable it in a legacy application we are not sure if it is completely
+backwardly compatible).
+
+=item *
+
C<psgi_middleware> - See L<PSGI MIDDLEWARE>.
=item *
my $env = $c->request->env;
if(my $query_obj = $env->{'plack.request.query'}) {
- $c->request->query_parameters($query_obj->as_hashref_mixed);
+ $c->request->query_parameters(
+ $c->request->_use_hash_multivalue ?
+ $query_obj->clone :
+ $query_obj->as_hashref_mixed);
return;
}
}
$env->{'plack.request.query'} ||= Hash::MultiValue->from_mixed(\%query);
- $c->request->query_parameters( \%query );
+ $c->request->query_parameters(
+ $c->request->_use_hash_multivalue ?
+ $env->{'plack.request.query'}->clone :
+ \%query);
}
=head2 $self->prepare_read($c)
}
}
+has _use_hash_multivalue => (
+ is=>'ro',
+ required=>1,
+ default=> sub {0});
+
# Amount of data to read from input on each pass
our $CHUNKSIZE = 64 * 1024;
Hash::MultiValue->new($query->flatten, $body->flatten);
};
+ if($self->_use_hash_multivalue) {
+ return $self->env->{'plack.request.merged'}->clone; # We want a copy, in case your App is evil
+ }
+
# We copy, no references
foreach my $name (keys %$query_parameters) {
my $param = $query_parameters->{$name};
$self->prepare_body if ! $self->_has_body;
return {} unless $self->_body;
- return $self->_body->param;
+ return $self->_use_hash_multivalue ?
+ $self->env->{'plack.request.body'}->clone :
+ $self->_body->param;
}
sub prepare_connection {
__PACKAGE__->config(
'Controller::Root', { namespace => '' },
+ use_hash_multivalue_in_request => 1,
);
__PACKAGE__->setup;
'expected content body /from_psgi_code_itr';
}
+{
+ ok my($res, $c) = ctx_request(POST '/test_psgi_keys?a=1&b=2', [c=>3,d=>4]);
+
+ ok $c->req->env->{"psgix.input.buffered"}, "input is buffered";
+ ok $c->req->env->{"plack.request.http.body"};
+ ok my $body = $c->req->env->{"plack.request.body"};
+ ok my $query = $c->req->env->{"plack.request.query"};
+ ok my $merged = $c->req->env->{"plack.request.merged"};
+
+ is $body->get('c'), 3;
+ is $body->get('d'), 4;
+ is $query->get('a'), 1;
+ is $query->get('b'), 2;
+ is $merged->get('c'), 3;
+ is $merged->get('d'), 4;
+ is $merged->get('a'), 1;
+ is $merged->get('b'), 2;
+}
+
done_testing;