diff --git a/lib/Dancer/Handler.pm b/lib/Dancer/Handler.pm index 0f5e6afe3..eaa586bb5 100644 --- a/lib/Dancer/Handler.pm +++ b/lib/Dancer/Handler.pm @@ -62,8 +62,11 @@ sub handle_request { Dancer::SharedData->request($request); # deserialize the request body if possible - $request = Dancer::Serializer->process_request($request) - if Dancer::App->current->setting('serializer'); + if (Dancer::App->current->setting('serializer')) { + $request = Dancer::Serializer->process_request($request); + return $self->render_response unless $request; + } + # read cookies from client diff --git a/lib/Dancer/Request.pm b/lib/Dancer/Request.pm index c6b58eb39..0b9bc2976 100644 --- a/lib/Dancer/Request.pm +++ b/lib/Dancer/Request.pm @@ -431,7 +431,8 @@ sub _build_params { %$previous, %{$self->{_query_params}}, %{$self->{_route_params}}, %{$self->{_body_params}}, }; - + # there may be new parameters in need of _decode()ing + $self->{_params_are_decoded} = 0; } # Written from PSGI specs: diff --git a/lib/Dancer/Serializer.pm b/lib/Dancer/Serializer.pm index 772e52623..cbb46ca0c 100644 --- a/lib/Dancer/Serializer.pm +++ b/lib/Dancer/Serializer.pm @@ -91,16 +91,21 @@ sub process_request { my $old_params = $request->params('body'); - # try to deserialize + # deserialize my $new_params; eval { $new_params = engine->deserialize($request->body) }; if ($@) { + Dancer::Error->new( + code => 400, + title => "Bad request", + message => "Deserialization failed", + )->render; Dancer::Logger::core "Unable to deserialize request body with " . engine() . " : \n$@"; - return $request; + return; } if(!ref $new_params or ref $new_params ne 'HASH'){ diff --git a/t/02_request/19_decode_added_params.t b/t/02_request/19_decode_added_params.t new file mode 100644 index 000000000..6c2b6d686 --- /dev/null +++ b/t/02_request/19_decode_added_params.t @@ -0,0 +1,50 @@ +use Dancer ':tests'; +use Dancer::Test; +use Test::More; +use Dancer::ModuleLoader; +use LWP::UserAgent; + +plan skip_all => "skip test with Test::TCP in win32" if $^O eq 'MSWin32'; +plan skip_all => 'Test::TCP is needed to run this test' + unless Dancer::ModuleLoader->load('Test::TCP' => "1.30"); + +plan skip_all => 'JSON is needed to run this test' + unless Dancer::ModuleLoader->load('JSON'); + +plan tests => 2; + +my $unicode = "\x{30dc}\x{a9}"; + +Test::TCP::test_tcp( + client => sub { + my $port = shift; + my $ua = LWP::UserAgent->new; + my $request = HTTP::Request->new( + POST => "http://127.0.0.1:$port/foo/$unicode", + [ + Host => 'localhost', + Content_Type => 'application/json', + ], + to_json({ foo => 'bar' }), + ); + my $res = $ua->request($request); + ok $res->is_success, 'Successful response from server'; + is $res->content, 1, 'Correct content'; + }, + server => sub { + my $port = shift; + use Dancer ':tests'; + set( + apphandler => 'Standalone', + port => $port, + show_errors => 0, + startup_info => 0, + charset => 'utf-8', + serializer => 'JSON', + ); + post '/foo/*' => sub { + params->{splat}[0] eq $unicode; + }; + Dancer->dance; + }, +); diff --git a/t/14_serializer/02_request_json.t b/t/14_serializer/02_request_json.t index 9f415708d..979de0ce4 100644 --- a/t/14_serializer/02_request_json.t +++ b/t/14_serializer/02_request_json.t @@ -14,13 +14,13 @@ BEGIN { set 'serializer' => 'JSON', 'show_errors' => 1; get '/' => sub { { foo => 'bar' } }; -post '/' => sub { params }; +post '/' => sub { scalar params }; put '/' => sub { param("id") }; get '/error' => sub { send_error( { foo => 42 }, 401 ) }; get '/error_bis' => sub { send_error( 42, 402 ) }; get '/json' => sub { content_type('application/json'); - to_json( { foo => 'bar' } ) + +{ foo => 'bar' }; }; response_content_is [ PUT => '/', @@ -41,8 +41,8 @@ my $res = dancer_response ( POST => '/', { - params => { foo => 1 }, - headers => [ 'Content-Type' => 'application/json' ] + headers => [ 'Content-Type' => 'application/json' ], + body => to_json({ foo => 1 }), } ); diff --git a/t/14_serializer/04_request_xml.t b/t/14_serializer/04_request_xml.t index 3302a2be9..d9fdd7690 100644 --- a/t/14_serializer/04_request_xml.t +++ b/t/14_serializer/04_request_xml.t @@ -34,8 +34,8 @@ SKIP: { my $res = dancer_response( POST => '/', { - params => { foo => 1 }, - headers => [ 'Content-Type' => 'text/xml' ] + headers => [ 'Content-Type' => 'text/xml' ], + body => to_xml({ foo => 1 }), } ); is_deeply( diff --git a/t/14_serializer/19_deserialization_failure.t b/t/14_serializer/19_deserialization_failure.t new file mode 100644 index 000000000..7de051e71 --- /dev/null +++ b/t/14_serializer/19_deserialization_failure.t @@ -0,0 +1,81 @@ +use Dancer ':tests'; +use Dancer::Test; +use Test::More; +use Dancer::ModuleLoader; +use LWP::UserAgent; + +plan skip_all => "skip test with Test::TCP in win32" if $^O eq 'MSWin32'; +plan skip_all => 'Test::TCP is needed to run this test' + unless Dancer::ModuleLoader->load('Test::TCP' => "1.30"); + +plan skip_all => 'JSON is needed to run this test' + unless Dancer::ModuleLoader->load('JSON'); + +plan tests => 3; + +set serializer => 'JSON'; + +my $data = { foo => 'bar' }; + +Test::TCP::test_tcp( + client => sub { + my $port = shift; + my $ua = LWP::UserAgent->new; + my $request = HTTP::Request->new( + PUT => "http://127.0.0.1:$port/deserialization", + [ + Host => 'localhost', + Content_Type => 'application/json' + ], + to_json($data), + ); + my $res = $ua->request($request); + ok $res->is_success, 'Successful response from server'; + is_deeply from_json($res->content), $data, 'Correct content'; + }, + server => sub { + my $port = shift; + use Dancer ':tests'; + set( + apphandler => 'Standalone', + port => $port, + show_errors => 0, + startup_info => 0, + ); + put '/deserialization' => sub { $data }; + Dancer->dance; + }, +); + +Test::TCP::test_tcp( + client => sub { + my $port = shift; + my $ua = LWP::UserAgent->new; + my $request = HTTP::Request->new( + PUT => "http://127.0.0.1:$port/deserialization", + [ + Host => 'localhost', + Content_Type => 'application/json' + ], + # broken JSON + '{ "foo": "bar", }', + ); + my $res = $ua->request($request); + is $res->code, 400, '400 bad request'; + }, + server => sub { + my $port = shift; + use Dancer ':tests'; + set( + apphandler => 'Standalone', + port => $port, + show_errors => 1, + startup_info => 0, + ); + put '/deserialization' => sub { $data }; + Dancer->dance; + }, +); + + +