Reputation: 85
I am building a simple API, which there is a point that when the ID entered in the endpoint URL does not point to a valid record, I get a standard NotFoundHttpException. And I cannot figure out how to override this in order to provide my own error message response as I do not wish to share my Model name etc.
Endpoint
Route::get('{mrl}', [MrlController::class, 'show']);
Controller
public function show(Mrl $mrl)
{
if ($data = $mrl) {
return response(['status' => 'ok', 'data' => $data], 200);
} else {
return response(['status' => 'error', 'message' => 'Could not retrieve data'], 500);
}
}
When I run this when a record exists I receive the following which is what I expect.
{
"status": "ok",
"data": {
"id": 98,
"market_id": 1,
"crop_id": 2,
"chemical_id": 113,
"maximum_residue_level": null,
"exempt": 0,
"comments": null,
"date_verified": "2021-10-07",
"created_at": "2021-10-19T05:42:12.000000Z",
"updated_at": "2021-10-19T05:42:12.000000Z"
}
}
However, when I enter an ID in the route endpoint for a record that does not exist, I receive the following:
{
"message": "No query results for model [App\\Seasonal\\Mrl] 99"
}
This is happening from what I understand to be the auto find of the record between the Route and the controller, and I am lost as to how to customize this behavior.
Thanks in advance for your help!
Upvotes: 1
Views: 1752
Reputation: 85
After spending time reading the docs I finally found the answer on how to customize the behaviour if a record is found when utilising Eloquents Route Model Binding.
In the Laravel docs, there is a method for this explicit purpose, to override the default behaviour when the bound model does not have a valid record.
The method for doing this is to chain the ->missing()
function call to the end of your route and then providing the behaviour for the response you wish to provide. As below:
Route::get('{mrl}', [MrlController::class, 'show'])->missing(function () {
return response(['status' => 'error', 'message' => 'Invalid query.'], 404);
});
Chaining this method onto my request has enabled me to return the generic response I was hoping for when querying an invalid model record.
Upvotes: 3
Reputation: 1322
you don't show the code where you are fetchning the model from the database but we can assume something like that:
$mrl = Mrl::findOrFail($id);
show($mrl);
The model findOrFail()
method throws an exception when the model is not found, which is convenient when you want to adapt the response.
You can imagine something like that:
try {
$mrl = Mrl::findOrFail($id);
return response(['status' => 'ok', 'data' => $data], 200);
} catch (ModelNotFoundException $e) {
return response(['status' => 'error', 'message' => 'Could not retrieve data'], 500);
}
The idea is to catch the error thrown by your model to change the message and status code of the response.
When building APIs you should event add a "generic" catch statement for any unhandled errors to display a standardized generic error message and log what happened, like this:
try {
$mrl = Mrl::findOrFail($id);
// Do more things that could generate errors ?
return response(['status' => 'ok', 'data' => $data], 200);
} catch (ModelNotFoundException $e) {
// Not found
return response(['status' => 'error', 'message' => 'Could not retrieve data'], 500);
} catch (\Exception $e) {
// Generic error
\Log::error($e);
return response(['status' => 'error', 'message' => 'An error occured'], 500);
}
Upvotes: 1