Skip to content

Commit a3be915

Browse files
committed
Correct 400 response to malformed json in POST
Symptom: ---------- Trying to create an object sending a seemingly correct JSON request in a POST message - was giving a 404 not found error. When looking at the cause in the browser devtools - the body of the response contained "Not found (input)" Cause: ------- We were trying to validate parameters - even when the POST body contained invalid JSON. We call json_decode($data) in retrieveInputs The JSON request was malformed and it had omitted the double quotes. example: { fieldName : "fieldValue" } Expected behavior and fix: --------------------------- A POST request with malformed JSON should get a 400 "bad request" response. Even more helpful would be an error code indicating why the JSON was bad. If we call json_decode(), afterwards, we immediately call json_last_error(), and if the return value does not match JSON_ERROR_NONE, we send it across bundled in a 400 response, so that the user may rectify the cause by looking at the exact error message. The possible error messages right now are: CODE CONSTANT-ERROR MESSAGE 0 JSON_ERROR_NONE-No error has occurred 1 JSON_ERROR_DEPTH-The maximum stack depth has been exceeded 2 JSON_ERROR_STATE_MISMATCH-Invalid or malformed JSON 3 JSON_ERROR_CTRL_CHAR-Control character error, possibly incorrectly encoded 4 JSON_ERROR_SYNTAX-Syntax error 5 JSON_ERROR_UTF8-Malformed UTF-8 characters, possibly incorrectly encoded 6 JSON_ERROR_RECURSION-One or more recursive references in the value to be encoded 7 JSON_ERROR_INF_OR_NAN-One or more NAN or INF values in the value to be encoded 8 JSON_ERROR_UNSUPPORTED_TYPE-A value of a type that cannot be encoded was given 9 JSON_ERROR_INVALID_PROPERTY_NAME-A property name that cannot be encoded was given 10 JSON_ERROR_UTF16-Malformed UTF-16 characters, possibly incorrectly encoded After the fix: ------------- On malformed JSON in the POST body - the following is returned: The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications. (Error decoding input JSON. json_last_error code: 4) If REQUEST_METHOD is not set - we throw an exception that says something like: 'Bad request (Error decoding input JSON. json_last_error code: 4)' In order to accommodate this change in behavior - in the test suite, we need a new method expectPattern (as the error code in the end of the string may change, but the starting pattern stays the same - (/^Bad request.*$/)
1 parent b746794 commit a3be915

File tree

3 files changed

+33
-1
lines changed

3 files changed

+33
-1
lines changed

api.php

+14
Original file line numberDiff line numberDiff line change
@@ -1270,6 +1270,15 @@ protected function exitWith404($type) {
12701270
}
12711271
}
12721272

1273+
protected function exitWith400($type) {
1274+
if (isset($_SERVER['REQUEST_METHOD'])) {
1275+
header('Content-Type:',true,400);
1276+
die("The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repeat the request without modifications. ($type)");
1277+
} else {
1278+
throw new \Exception("Bad request ($type)");
1279+
}
1280+
}
1281+
12731282
protected function exitWith422($object) {
12741283
if (isset($_SERVER['REQUEST_METHOD'])) {
12751284
header('Content-Type:',true,422);
@@ -1684,6 +1693,11 @@ protected function retrieveInputs($data) {
16841693
$input = false;
16851694
} else if ($data[0]=='{' || $data[0]=='[') {
16861695
$input = json_decode($data);
1696+
$causeCode = json_last_error();
1697+
if ($causeCode !== JSON_ERROR_NONE) {
1698+
$errorString = "Error decoding input JSON. json_last_error code: " . $causeCode;
1699+
$this->exitWith400($errorString);
1700+
}
16871701
} else {
16881702
parse_str($data, $input);
16891703
foreach ($input as $key => $value) {

tests/Api.php

+18
Original file line numberDiff line numberDiff line change
@@ -138,4 +138,22 @@ public function expect($output, $error=false)
138138
}
139139
return $this;
140140
}
141+
142+
public function expectPattern($expectedOutputPattern, $expectedErrorPattern) {
143+
$exception = false;
144+
ob_start();
145+
try {
146+
$this->api->executeCommand();
147+
} catch (\Exception $e) {
148+
$exception = $e->getMessage();
149+
}
150+
$outputData = ob_get_contents();
151+
ob_end_clean();
152+
if ($exception) {
153+
$this->test->assertRegExp($expectedErrorPattern, $exception);
154+
} else {
155+
$this->test->assertRegExp($expectedOutputPattern, $outputData);
156+
}
157+
return $this;
158+
}
141159
}

tests/Tests.php

+1-1
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ public function testErrorOnInvalidJson()
290290
{
291291
$test = new Api($this);
292292
$test->post('/posts', '{"}');
293-
$test->expect(false, 'Not found (input)');
293+
$test->expectPattern(false, '/^Bad request.*$/');
294294
}
295295

296296
public function testErrorOnDuplicatePrimaryKey()

0 commit comments

Comments
 (0)