In which place is best practice to validate parameters sent by the user in an API design? By parameter validation I refer to: checking required params are sent, ensure they have correct format and so... Here are a couple of simple examples that validate an id
has been sent. It is Python using Flask to illustrate:
A) Add validation logic in the route definition, within the controller.
@api.route('/job', methods=['GET'])
def get_jobs():
try:
if params["id"] is None:
raise Exception("Invalid param ID parameters.")
job = job_manager.get_job(params["id"])
return jsonify(job)
B) In the core of the app. This is the business layer, where logic is applied to transform data.
class JobManager:
def get_job(self, job_id) -> None:
if job_id is None:
raise Exception("Invalid param ID parameters.")
In more complex scenarios a validator
service or decorators could be used, but the question would be the same: At which point of the code is best practice to validate a user's input.
If the answer is none of the scenarios above (or both), please provide more details on your answer. If possible, try to be language agnostic as I'm looking for a best practice that can be applied anywhere.
CodePudding user response:
Generally, I split validation into phases:
- Immediate syntax validation of input data in the REST controller
- Business-logic validation in the services
The first validation should only flag things that are definitely wrong; e.g. missing required fields, type mismatch, unparsable strings, any attempts of code injection and the presence (or lack of) of security tokens.
When this validation passes, the input data is at least syntactically correct and may be passed on to the services, where a more strict validation occurs; i.e. does the input data make sense business-wise, does the resource with that ID exist - and so on.
Short version: The first validation looks for things that are obviously wrong, while the following validation ensures input data is correct and meaningful business-wise.
CodePudding user response:
Parsing, as a rule, should happen at the point where information enters your system, or as close to that point as is practical.
Therefore certainly "application layer" rather than "domain layer/business layer": either invoked by the controller itself, or very close to it. (Not typically "in" the controller, because you should be able to test the parser without being coupled to a bunch of HTTP ceremony.)
@api.route('/job', methods=['GET'])
def get_jobs():
try:
job_id = parse_job_id(params["id"])
job = job_manager.get_job(job_id)
return jsonify(job)
In typed languages, this can make your life a lot easier, because you greatly reduce the number of places you have to ask "does this general purpose data structure have the information I expect?"
Checks against business policy, on the other hand, normally belong in the domain layer.
For example: if your API requires a date, the checks that the date is actually present, and that the date is represented in the appropriate ISO-8601 format, and so on... these kinds of checks all happen as part of the parsing of the input by the controller.
On the other hand, checking that the date is "in the future", or that the date is within warranty, or ... these are checks that belong in your domain code.