Handling JSON data errors in Slim 3
When you send JSON data into a Slim Framework application with a content-type of application/json, then Slim will decode it for you if you use getParsedBody():
$app->post("/", function ($request, $response, $args) {
$input = $request->getParsedBody();
var_dump($input);exit;
});
Using curl to test:
$ curl -H "Content-Type: application/json" http://localhost:8888 -d '{"foo": "bar"}'
array(1) {
'foo' =>
string(3) "bar"
}
If there’s an error however, you get this:
$ curl -H "Content-Type: application/json" http://localhost:8888 -d '{foo: bar}'
NULL
If you care about this, you can use json_last_error_msg() and return an error:
$app->post("/", function ($request, $response, $args) {
$input = $request->getParsedBody();
if ($input === null) {
return $response->withJson(
['error_decoding_json' => json_last_error_msg()],
400,
JSON_PRETTY_PRINT
);
}
var_dump($input);exit;
});
(note – in real code, you should check that the Accept header was a JSON one…)
Don’t forget the JSON_PRETTY_PRINT as a human is going to be reading this error, so make it easier for them.
Use jsonlint for more information
If you really want to provide great diagnostics, then use jsonlint:
$ composer require seld/jsonlint
Update your handler like this:
$app->post("/", function ($request, $response, $args) {
$input = $request->getParsedBody();
if ($input === null) {
$parser = new \Seld\JsonLint\JsonParser();
$result = $parser->lint($input);
if ($result instanceof \Seld\JsonLint\ParsingException) {
return $response->withJson(
['error_decoding_json' => $result->getMessage()],
400,
JSON_PRETTY_PRINT
);
}
}
var_dump($input);exit;
});
(lint() will return NULL or a ParsingException, so we don’t need to test for anything else.)
The result looks like this:
$ curl -H "Content-Type: application/json" http://localhost:8888 -d '{foo: bar}'
{
"error_decoding_json": "Parse error on line 1:\n\n^\nExpected one of: 'STRING', 'NUMBER', 'NULL', 'TRUE', 'FALSE', '{', '['"
}
This is much more informative!
I think this should be something like this:
"`
class JsonLintMiddleware {
public function __invoke(Request $request, Response $response, $next) {
$input = $request->getBody();
$parser = new JsonParser();
$result = $parser->lint($input);
if ($result instanceof ParsingException) {
return $response->withJson(
['message' => $result->getMessage()],
400,
JSON_PRETTY_PRINT
);
}
$response = $next($request, $response);
return $response;
}
}
"`