来自客户端的错误数据通过了GraphQL验证

时间:2020-05-20 12:42:05

标签: node.js reactjs graphql apollo nestjs

我已经在带有GraphQL API的NestJS服务器上使用React和Apollo客户端制作了简单的CRUD应用。

我有这个简单的突变:

schema.gql:

type Mutation {
  createUser(input: CreateUserInput!): User! // CreateUserInput type you can see in user.input.ts below
  updateUser(id: ID!, input: UpdateUserInput!): User!
  deleteUser(id: ID!): User!
}

user.input.ts:

import { InputType, Field } from "@nestjs/graphql";
import { EmailScalar } from "../email.scalar-type";

@InputType()
export class CreateUserInput {
    // EmailScalar is a custom Scalar GraphQL Type that i took from the internet and it worked well
    @Field(() => EmailScalar)
    readonly email: string;
    @Field()
    readonly name: string;
}

“ EmailScalar”类型检查“电子邮件”输入是否基本上具有*@*.*格式

当我像这样对GraphQL API进行createUser查询时:

It cannot pass validation (因为电子邮件类型可以正常工作)


但是当客户端发送查询时-它通过了验证:

NestJS server log(来自下面的代码)

users.resolver.ts:

@Mutation(() => User)
async createUser(@Args('input') input: CreateUserInput) { // Type from user.input.ts
   Logger.log(input); // log from screenshot, so if it's here it passed validation
   return this.usersService.create(input); // usersService makes requests to MongoDB
}

And it gets into MongoDB


这是客户端部分:

App.tsx:

...

// CreateUserInput class is not imported to App.tsx (it is at server part) but it seems to be fine with it
const ADD_USER = gql`
  mutation AddMutation($input: CreateUserInput!) {
    createUser(input: $input) {
      id
      name
      email
    }
  }
`

function App(props: any) {
  const { loading, error, data } = useQuery(GET_USERS);

  const [addUser] = useMutation(
    ADD_USER,
    {
      update: (cache: any, { data: { createUser } }: any) => {
        const { users } = cache.readQuery({ query: GET_USERS });
        cache.writeQuery({
          query: GET_USERS,
          data: {
            users: [createUser, ...users],
          },
        })
      }
    }
  );

...

  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error :(</p>;

  return <UserTable users={data.users} addUser={addUser} updateUser={updateUser} deleteUser={deleteUser} />;
}

有人可以向我解释一下,客户端查询如何通过验证,我做错了什么?

两个空字符串都可以通过。

以前从未使用过NestJS,Apollo,React或GraphQL,所以我有点迷茫。

有关完整代码: https://github.com/N238635/nest-react-crud-test

1 个答案:

答案 0 :(得分:1)

这是定义自定义标量方法的方式:

parseValue(value: string): string {
  return value;
}

serialize(value: string): string {
  return value;
}

parseLiteral(ast: ValueNode): string {
  if (ast.kind !== Kind.STRING) {
    throw new GraphQLError('Query error: Can only parse strings got a: ' + ast.kind, [ast]);
  }

  // Regex taken from: http://stackoverflow.com/a/46181/761555
  var re = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
  if (!re.test(ast.value)) {
    throw new GraphQLError('Query error: Not a valid Email', [ast]);
  }

  return ast.value;
}
解析查询内部的文字值(即用双引号引起来的文字字符串)时,会调用

parseLiteral。解析变量值时调用parseValue。当您的客户端发送查询时,它将值作为变量而不是文字值发送。因此,使用parseValue代替parseLiteral。但是您的parseValue不会进行任何形式的验证-您只按原样返回值。您需要在两种方法中都使用验证逻辑。

实现serialize方法也是一个好主意,这样您的标量可以用于输入和响应验证。