在哪里验证发送到 API 的参数?
Where to validate parameters sent to an API?
在 API 设计中,验证用户发送的参数的最佳做法是在哪个位置?通过参数验证,我指的是:检查所需的参数是否已发送,确保它们具有正确的格式等等......这里有几个简单的例子来验证 id
已经发送。就是Python使用Flask来说明:
A) 在控制器内的路由定义中添加验证逻辑。
@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) 在应用程序的核心。这是业务层,其中应用逻辑来转换数据。
class JobManager:
def get_job(self, job_id) -> None:
if job_id is None:
raise Exception("Invalid param ID parameters.")
在更复杂的场景中,可以使用 validator
服务或装饰器,但问题是相同的:代码的哪一点是验证用户输入的最佳实践。
如果答案是上述场景中的 none 个(或两者),请提供有关您答案的更多详细信息。如果可能,请尝试与语言无关,因为我正在寻找可以在任何地方应用的最佳实践。
通常,我将验证分为几个阶段:
- REST 控制器中输入数据的即时语法验证
- 服务中的业务逻辑验证
第一次验证应该只标记绝对错误的事情;例如缺少必填字段、类型不匹配、无法解析的字符串、任何代码注入尝试以及是否存在(或缺少)安全令牌。
当此验证通过时,输入数据至少在语法上是正确的,并且可以传递给服务,在那里进行更严格的验证;即输入数据在商业上是否有意义,具有该 ID 的资源是否存在 - 等等。
简短版本:第一个验证查找明显错误的内容,而随后的验证确保输入数据正确且对业务有意义。
Parsing,通常,应该发生在信息进入您的系统的那一点,或者尽可能接近那一点。
因此肯定是“应用层”而不是“域 layer/business 层”:要么由控制器本身调用,要么非常接近它。 (通常不“在”控制器中,因为您应该能够在不耦合到一堆 HTTP 仪式的情况下测试解析器。)
@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)
在类型化语言中,这可以让你的生活更轻松,因为你大大减少了你必须问“这个通用数据结构是否有我期望的信息?”的地方的数量。
另一方面,检查业务策略,通常属于域层。
例如:如果您的 API 需要日期,系统会检查日期是否实际存在,以及日期是否以适当的 ISO-8601 格式表示,等等...这些类型所有检查都是控制器解析输入的一部分。
另一方面,检查日期是否在“未来”,或者日期是否在保修期内,或者...这些检查属于您的域代码。
在 API 设计中,验证用户发送的参数的最佳做法是在哪个位置?通过参数验证,我指的是:检查所需的参数是否已发送,确保它们具有正确的格式等等......这里有几个简单的例子来验证 id
已经发送。就是Python使用Flask来说明:
A) 在控制器内的路由定义中添加验证逻辑。
@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) 在应用程序的核心。这是业务层,其中应用逻辑来转换数据。
class JobManager:
def get_job(self, job_id) -> None:
if job_id is None:
raise Exception("Invalid param ID parameters.")
在更复杂的场景中,可以使用 validator
服务或装饰器,但问题是相同的:代码的哪一点是验证用户输入的最佳实践。
如果答案是上述场景中的 none 个(或两者),请提供有关您答案的更多详细信息。如果可能,请尝试与语言无关,因为我正在寻找可以在任何地方应用的最佳实践。
通常,我将验证分为几个阶段:
- REST 控制器中输入数据的即时语法验证
- 服务中的业务逻辑验证
第一次验证应该只标记绝对错误的事情;例如缺少必填字段、类型不匹配、无法解析的字符串、任何代码注入尝试以及是否存在(或缺少)安全令牌。
当此验证通过时,输入数据至少在语法上是正确的,并且可以传递给服务,在那里进行更严格的验证;即输入数据在商业上是否有意义,具有该 ID 的资源是否存在 - 等等。
简短版本:第一个验证查找明显错误的内容,而随后的验证确保输入数据正确且对业务有意义。
Parsing,通常,应该发生在信息进入您的系统的那一点,或者尽可能接近那一点。
因此肯定是“应用层”而不是“域 layer/business 层”:要么由控制器本身调用,要么非常接近它。 (通常不“在”控制器中,因为您应该能够在不耦合到一堆 HTTP 仪式的情况下测试解析器。)
@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)
在类型化语言中,这可以让你的生活更轻松,因为你大大减少了你必须问“这个通用数据结构是否有我期望的信息?”的地方的数量。
另一方面,检查业务策略,通常属于域层。
例如:如果您的 API 需要日期,系统会检查日期是否实际存在,以及日期是否以适当的 ISO-8601 格式表示,等等...这些类型所有检查都是控制器解析输入的一部分。
另一方面,检查日期是否在“未来”,或者日期是否在保修期内,或者...这些检查属于您的域代码。