GraphQL 和表单验证错误

Posted

技术标签:

【中文标题】GraphQL 和表单验证错误【英文标题】:GraphQL and form validation errors 【发布时间】:2015-12-16 05:13:57 【问题描述】:

假设您有一个将数据发布到 API 服务器的表单。 API 服务器验证输入并返回 JSON 对象。如果输入无效,则返回如下错误对象。

errors: field1: "is required"

在使用 GraphQL 时,我们如何处理和处理此类错误?应该如何以及在哪里实现数据验证(应该是 GraphQL 的一部分还是应该在每个解析函数中)?

【问题讨论】:

【参考方案1】:

通过resolve 方法中的验证逻辑,您可以完全控制生成的用户错误。这是一个例子:

// data/mutations/createUser.js
import 
  GraphQLObjectType as ObjectType,
  GraphQLNonNull as NonNull,
  GraphQLList as List,
  GraphQLString as StringType
 from 'graphql';
import validator from 'validator';
import UserType from '../types/UserType';

export default 
  type: new ObjectType(
    name: 'CreateUserResult',
    fields: 
      user:  type: UserType ,
      errors:  type: new NonNull(new List(StringType)) 
    
  ),
  args: 
    email:  type: new NonNull(StringType) ,
    password:  type: new NonNull(StringType) 
  ,
  resolve(_,  email, password ) 
    let user = null;
    let errors = [];

    if (validator.isNull(email)) 
      errors.push(...['email', 'The email filed must not be empty.']);
     else if (!validator.isLength(email,  max: 100)) 
      errors.push(...['email', 'The email must be at a max 100 characters long.']);
    

    // etc.

    return  user, errors ;
  
;

请参阅我关于此主题的博文 - Validation and User Errors in GraphQL Mutations

或者,创建type UserErrorType key: String!, message: String! ,在编译要返回给调用者的用户错误列表时,可以使用它来代替纯字符串。

GraphQL 查询

mutation 
  createUser(email: "hello@tarkus.me", password: "Passw0rd") 
    user  id, email ,
    errors  key, message 
  

查询响应


  data: 
    user: null,
    errors: [
       key: '', message: 'Failed to create a new user account.' ,
       key: 'email', message: 'User with this email already exists.' 
    ]
  

【讨论】:

这可能是我们将获得的最佳临时解决方案,直到/除非 GraphQL 选择单独的错误对象来区分系统错误和用户字段/消息验证。我所做的唯一细微更改是创建一个专用的ErrorType,它具有fieldmsg 键,因此使用它比关心数组插入顺序要容易一些......但它最终是一样的事情。 您还可以将验证端点引入 GraphQL 架构本身,或者在结果集中返回验证节点。【参考方案2】:

检查这个包。它可以通过 graphql 响应上的 errors 数组轻松发送机器可读的错误。然后,您可以将错误反馈到您的前端并采取措施和/或提醒用户发生了什么事:

https://github.com/thebigredgeek/apollo-errors

【讨论】:

【参考方案3】:

我使用一个小包 - graphql-validation 在我的项目中验证表单。它的包装validator.js。很容易使用。

例子:

const  validator, validate  = require('graphql-validation'); // Import module

const resolver = 
  Mutation: 
    createPost: validator([ // <-- Validate here
      validate('title').not().isEmpty( msg: 'Title is required' ),
      validate('content').isLength( min: 10, max: 20 ),
    ], (parent, args, context, info) => 
      if (context.validateErrors.length > 0) 
        // Validate failed
        console.log(context.validateErrors); // Do anything with this errors

        return;
      

      // Validate successfully, time to create new post
    ),
  ,
;
Input:  title: '', content: 'Hi!' 

// console.log(context.validateErrors);
Output: [
   param: 'title', msg: 'Title is required' ,
   param: 'content', msg: 'Invalid value' ,
]

希望有用。

【讨论】:

【参考方案4】:

我创建了一个npm module 以更好地处理 GraphQL 中的验证。请查看validate-graphql npm 包。

【讨论】:

【参考方案5】:

最好将验证/功能检查放入服务层。

GraphQL 只是应用程序的一个入口点。因此它不应该进行验证和能力检查。

如果您想到一个具有多个访问层(REST 和 GraphQL)的应用程序。您将通过在 GraphQL 层中添加验证检查来复制代码。

最好的方法是有一个代码层来处理这个问题,例如 UserService。这将保存您的验证和能力检查逻辑。

GraphQL 和 REST API 只是将响应转换为相应响应类型的可接受格式的格式化程序。以下示例用于说明目的:

class UserService 
    public function updateName(string $name) 
        // validation/capability check code here.
        // if validation fails, throw a user input exception or appropriate exception 
        //return value.
    

GraphQl Mutation
class UserResolver 
    public function updateUserName(array $args, context $context) 
        try 
            $user = (new UserService() )->updateName(args['name']);
            return [
                'user' => $user
            ];
         catch (UserInputException $exception) 
            return [
                'error' => $exception,
                'user' => null
            ];
        
    

REST API Controller
class UserController 
    public function updateUserName(string $name) 
        try 
            $user = (new UserService() )->updateName($name);

            return [
                'user' => $user
            ];
         catch (UserInputException $exception) 
            return [
                'error' => $exception->message,
            ];
        
    

通过这种方式在 Service 类中使用异常,您还可以选择要在响应中返回的异常(可以是 GraphQL 或 REST 响应)。

我们应该只将 GraphQL 视为访问层。解析器函数应尽可能简单/简单,并且不包含业务逻辑、验证和能力检查。

【讨论】:

以上是关于GraphQL 和表单验证错误的主要内容,如果未能解决你的问题,请参考以下文章

Angular 4 表单验证和错误消息

从 GraphQL-Java 返回错误

Django Formset管理-表单验证错误

jQuery 不显眼的表单验证器在重置表单时不会隐藏错误消息

表单验证 - Django 中的注册和登录错误处理

Django:模板中的 Crispy 表单验证错误