Current Functionality
Hello,
We are building a REST API using Symfony 3.3 and want to be able to output any type of error information (both exceptions and API-specific errors) back to the requester as a JSON response.
We have already installed a bundle to take care of API Exception handling. (It will catch normal built-in exceptions such as 404, and return their status codes with a brief description in JSON format).
What this bundle does is that, for example, if we go to a route that doesn't exist, it will pass back a JSON object similar to the following:
{ "errorCode": 404, "errorMessage": "No route found for /testing" }
The New Functionality Which Is Needed
Now that all normal exceptions are caught and displayed in JSON format, we want to be able to catch when the user uses our API incorrectly and then alert them to how they can fix their request via the JSON response we send them.
We want to catch API-usage errors, such as if the user is:
- Missing required parameters.
- Passing in the incorrect type of parameter.
We have a lot of different actions in the controllers which each accept a different parameter list, and some may not accept parameters at all.
We want to be able to catch both incorrect parameters in the route itself (for example, the first part of our route starts with the language code - i.e. something like http://localhost/en/someAction) and we also want to catch incorrect parameters added as a query - i.e. if they do something like http://localhost/en/someAction?firstName=123. Obviously in this case, we would want a string passed into the firstName parameter.
Current Proposed Solution
A. We imagine that we'll need some sort of global mapping array which will hold the data of what the correct parameter names should be for each controller action (storing this information in the database is likely not an option at this point). For example, I was thinking we could have an associative array something like the following:
[ "addUserAction" => ["firstName|str|req", "lastName|str|opt"],
"deleteUserAction" => ["userId|int|req"] ]
B. The only thing is, we not only want to perform these checks at the controller action level, but we also want to perform these checks in each of the repository functions in order to provide an extra layer of security. So, we'd have to actually add additional data to the array above to differentiate between whether a controller action is being executed or a repository's function. Perhaps something like:
[ "ctl.user.addUserAction" => ["firstName|str|req", "lastName|str|opt"],
"repo.user.deleteUser" => ["userId|int|req"] ]
C. This mapping array would then need to be stored somewhere globally where both controllers and repositories can access it.
D. Once we're able to access the mapping array from both controllers and repositories, we would then lookup the current function we're in within the array and then we'd know what the correct parameters to accept are and could decide whether to throw a JSON exception or not.
Questions
Q1. A Better Solution?
Looking at my proposed plan above, even if I'm able to implement it, it seems like an awful lot of overhead work (and a very coupled design which will involve that array having to be added to constantly whenever we add new functionality). I imagine there is a smarter way to not only perform the mapping in Symfony but also to check the mapping at the appropriate time without having to edit each and every function to check against the associate array (which is why I'm reaching out for help here before I begin coding this). I would appreciate any feedback on how this proposed solution could be made better and more maintainable.
Q2. Global Associative Arrays
Assuming that for some reason that we did want to go with the global associative array idea proposed above, out of curiosity, where would the proper place be in Symfony to place such an array so that it can be accessed globally (i.e. by both a controller's action or repository's function)? (As a PHP Developer coming from a CodeIgniter-background, I know that in CodeIgniter, we would have placed things we wanted to use across the application in the constants.php file. No idea what the equivalent functionality would be in Symfony).