Skip to content

Commit e144f80

Browse files
Petestalgebhardt
authored andcommitted
Rescue JSON parsing error and return all exceptions from the parser in _parser_exception
1 parent d35f2e5 commit e144f80

5 files changed

Lines changed: 75 additions & 3 deletions

File tree

lib/jsonapi/error_codes.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ module JSONAPI
2020
INVALID_FILTERS_SYNTAX = '120'
2121
SAVE_FAILED = '121'
2222
INVALID_DATA_FORMAT = '122'
23+
BAD_REQUEST = '400'
2324
FORBIDDEN = '403'
2425
RECORD_NOT_FOUND = '404'
2526
NOT_ACCEPTABLE = '406'

lib/jsonapi/exceptions.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,32 @@ def errors
119119
end
120120
end
121121

122+
class BadRequest < Error
123+
def initialize(exception)
124+
@exception = exception
125+
end
126+
127+
def errors
128+
[JSONAPI::Error.new(code: JSONAPI::BAD_REQUEST,
129+
status: :bad_request,
130+
title: I18n.translate('jsonapi-resources.exceptions.bad_request.title',
131+
default: 'Bad Request'),
132+
detail: I18n.translate('jsonapi-resources.exceptions.bad_request.detail',
133+
default: @exception))]
134+
end
135+
end
136+
137+
class InvalidRequestFormat < Error
138+
def errors
139+
[JSONAPI::Error.new(code: JSONAPI::BAD_REQUEST,
140+
status: :bad_request,
141+
title: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.title',
142+
default: 'Bad Request'),
143+
detail: I18n.translate('jsonapi-resources.exceptions.invalid_request_format.detail',
144+
default: 'Request must be a hash'))]
145+
end
146+
end
147+
122148
class ToManySetReplacementForbidden < Error
123149
def errors
124150
[JSONAPI::Error.new(code: JSONAPI::FORBIDDEN,

lib/jsonapi/mime_types.rb

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require 'json'
2+
13
module JSONAPI
24
MEDIA_TYPE = 'application/vnd.api+json'
35

@@ -19,9 +21,18 @@ def self.install
1921

2022
def self.parser
2123
lambda do |body|
22-
data = JSON.parse(body)
23-
data = {:_json => data} unless data.is_a?(Hash)
24-
data.with_indifferent_access
24+
begin
25+
data = JSON.parse(body)
26+
if data.is_a?(Hash)
27+
data.with_indifferent_access
28+
else
29+
fail JSONAPI::Exceptions::InvalidRequestFormat.new
30+
end
31+
rescue JSON::ParserError => e
32+
{ _parser_exception: JSONAPI::Exceptions::BadRequest.new(e.to_s) }
33+
rescue => e
34+
{ _parser_exception: e }
35+
end
2536
end
2637
end
2738
end

lib/jsonapi/request_parser.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ def setup_action(params)
3434

3535
setup_action_method_name = "setup_#{params[:action]}_action"
3636
if respond_to?(setup_action_method_name)
37+
raise params[:_parser_exception] if params[:_parser_exception]
3738
send(setup_action_method_name, params)
3839
end
3940
rescue ActionController::ParameterMissing => e

test/integration/requests/request_test.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,39 @@ def test_put_content_type
353353
assert_match JSONAPI::MEDIA_TYPE, headers['Content-Type']
354354
end
355355

356+
def test_put_valid_json
357+
put '/posts/3', params: '{"data": { "type": "posts", "id": "3", "attributes": { "title": "A great new Post" } } }',
358+
headers: {
359+
'CONTENT_TYPE' => JSONAPI::MEDIA_TYPE,
360+
'Accept' => JSONAPI::MEDIA_TYPE
361+
}
362+
363+
assert_equal 200, status
364+
end
365+
366+
def test_put_invalid_json
367+
put '/posts/3', params: '{"data": { "type": "posts", "id": "3" "attributes": { "title": "A great new Post" } } }',
368+
headers: {
369+
'CONTENT_TYPE' => JSONAPI::MEDIA_TYPE,
370+
'Accept' => JSONAPI::MEDIA_TYPE
371+
}
372+
373+
assert_equal 400, status
374+
assert_equal 'Bad Request', json_response['errors'][0]['title']
375+
assert_match 'unexpected token at', json_response['errors'][0]['detail']
376+
end
377+
378+
def test_put_valid_json_but_array
379+
put '/posts/3', params: '[{"data": { "type": "posts", "id": "3", "attributes": { "title": "A great new Post" } } }]',
380+
headers: {
381+
'CONTENT_TYPE' => JSONAPI::MEDIA_TYPE,
382+
'Accept' => JSONAPI::MEDIA_TYPE
383+
}
384+
385+
assert_equal 400, status
386+
assert_equal 'Request must be a hash', json_response['errors'][0]['detail']
387+
end
388+
356389
def test_patch_content_type
357390
patch '/posts/3', params:
358391
{

0 commit comments

Comments
 (0)