石墨烯的输入验证库
项目描述
石墨烯输入验证器
重要:这是一个概念证明,很可能还没有准备好用于生产。
GraphQL Python 生态系统(即graphene)缺乏正确的方法来验证输入并将有意义的错误返回给客户端。这个 PoC 旨在解决这个问题。extensions由于错误消息,客户端将知道它需要查看验证错误ValidationError。
这个库提供了一个类装饰器@validated,用于突变,允许字段级别和输入级别验证,类似于DRF序列化程序的validate方法。要验证一个字段,您需要声明一个名为validate_{field_name}. 可以在该validate方法中执行输入范围的验证(例如,对于依赖于其他字段的字段)。validate只有在所有字段级验证方法都成功时才会调用。
它还支持递归验证,因此您可以使用嵌套InputField的 s 并且验证将一直执行到标量。
为了指示一个无效值,相应的验证方法应该引发一个子类的实例ValidationError。验证方法还允许动态操作值(例如,通过交换相应对象的 ID 来最小化 DB 查询),然后替换主输入中的相应值(用于validate和突变本身)。
子ValidationError类可以报告一个或多个验证错误。它的error_details属性必须是可迭代的字典,提供验证错误的详细信息。错误详细信息映射可以包含任何成员,但作为约定code成员,鼓励包含在内。
对于字段级错误,将使用一个path成员修改错误详细信息,该成员可帮助客户端确定哪个输入片段无效,这对于 UI 上的丰富表单和字段突出显示很有用。
SingleValidationError为仅包含单个错误详细信息的验证错误提供了一个类。此类还支持meta错误详细信息属性,以通知客户端输入本身的潜在约束。
请注意,不支持详细消息,因为我坚信应该在客户端处理这些消息(连同本地化)。
用法
验证突变的输入
这是一个示例用法(您也可以在tests.py中找到):
import graphene
from graphene_validator.decorators import validated
class TestInput(graphene.InputObjectType):
email = graphene.String()
people = graphene.List(PersonalDataInput)
numbers = graphene.List(graphene.Int)
person = graphene.InputField(PersonalDataInput)
@staticmethod
def validate_email(email, info, **input):
if "@" not in email:
raise InvalidEmailFormat
return email.strip(" ")
@staticmethod
def validate_numbers(numbers, info, **input):
if len(numbers) < 2:
raise LengthNotInRange(min=2)
for n in numbers:
if n < 0 or n > 9:
raise NotInRange(min=0, max=9)
return numbers
@staticmethod
def validate(input, info):
if input.get("people") and input.get("email"):
first_person_name_and_age = (
f"{input['people'][0]['the_name']}{input['people'][0]['the_age']}"
)
if input["email"].split("@")[0] != first_person_name_and_age:
raise NameAndAgeInEmail
return input
@validated
class TestMutation(graphene.Mutation):
class Arguments:
input = TestInput()
result = graphene.String()
def mutate(self, _info, input):
return TestMutation(result="ok"))
这是一个示例输出:
{
"errors": [
{
"message": "ValidationError",
...
"extensions": {
"validationErrors": [
{
"code": "InvalidEmailFormat",
"path": [
"email"
]
},
{
"code": "LengthNotInRange",
"path": [
"people",
0,
"name"
],
"meta": {"min": 1, "max": 300}
}
]
}
}
],
...
}
验证依赖于其他字段或请求上下文的字段
class TestInput(graphene.InputObjectType):
first_field = graphene.String()
second_field = graphene.String()
@staticmethod
def validate_first_field(first_field, info, **input):
second_field = input.get("second_field")
if second_field != "desired value":
raise InvalidSecondField
if info.context.user.role != "admin":
raise Unauthorized
return first_field
...
运行测试
pip install -e .
pytest tests.py
限制
由于错误列在extensions泛型字段中GraphQLError,而不是使用典型的基于联合的错误,因此不会自动发现错误。理想的解决方案是允许修饰突变并获得可由客户端用于自动发现错误类型和元数据的联合的混合。
将一个示例 graphene-django 查询添加到schema.py以允许客户端发现错误类型及其元数据(后者是 TODO)。