GraphQL Java - Scalars

GraphQL中的Scalar

Scalar(原子类型)

在GraphQL类型系统中,类型树的叶子节点成为Scalar。一旦访问到了Scalar类型的数据,就无法在该类型基础上进一步访问其下的类型层次结构。Scalar类型意味着该类型的值无法再细分。

在GraphQL规范中,要求其所有实现都必须具有如下Scalar类型:

  • String类型(GraphQLString):UTF-8编码的字符序列
  • Boolean类型(GraphQLBoolean):true或false
  • Int类型(GraphQLInt):32位的有符号类型
  • Float类型(GraphQLFloat):单精度浮点类型
  • ID类型(GraphQLID):唯一标识符类型(可以被序列化为String)。但ID类型的数据可读性较差。

GraphQL - Java补充添加了如下几种额外类型:

  • Long类型(GraphQLLong):基于java.lang.Long的scalar类型
  • Short类型(GraphQLShort):基于java.lang.Short的scalar类型
  • Byte类型(GraphQLByte):基于java.lang.Byte的scalar类型
  • BigDecimal类型(GraphQLBigDecimal):基于java.math.BigDecimal的scalar类型
  • BigInteger类型(GraphQLBigInteger):基于java.math.BigInteger的scalar类型

graphql.Scalars类中包含了所有现有的scalar类型的单例实例。

编写自定义的Scalar类型

可以自定义Scalar类型。在这种实现方式下,需要在运行时自己实现数据到类型的映射机制。

假设我们有一个email的scalar类型,它将使用email地址作为输出和输出。

创建一个EMAIL的scalar类型如下:

        public static final GraphQLScalarType EMAIL = new GraphQLScalarType("email", "A custom scalar that handles emails", new Coercing() {
            @Override
            public Object serialize(Object dataFetcherResult) {
                return serializeEmail(dataFetcherResult);
            }

            @Override
            public Object parseValue(Object input) {
                return parseEmailFromVariable(input);
            }

            @Override
            public Object parseLiteral(Object input) {
                return parseEmailFromAstLiteral(input);
            }
        });

数据映射

自定义的scalar视线中,核心工作是数据映射的实现部分。主要需要实现如下三个方法:

  • parseValue:接收一个input变量,然后在运行时转换为Java中的对象。
  • parseLiteral:接收一个AST常量(graphql.language.Value类型)作为输入,在运行时转换为Java中的对象。
  • serialize:接收一个Java对象,将它转换为scalar中的output表示形式。

自定义的scalar中,必须要实现两个input的转换(parseValue / parseLiteral)和一个output的转换(serialize)。

例如,对于如下的执行语句:

    mutation Contact($mainContact: Email!) {
      makeContact(mainContactEmail: $mainContact, backupContactEmail: "backup@company.com") {
        id
        mainContactEmail
      }
    }

自定义的Email类型scalar将会有如下调用:

  • parseValue方法被调用,将$mailContact变量转换为Java中的运行时数据。
  • parseLiterial方法被调用,将AST的graphql.language.StringValue(backup@company.com)转换为Java中的运行时数据。
  • serialise方法被调用,将mainContactEmail的运行时表示,转换为graphQL的输出形式。

输入和输出有效性验证

scalar定义中也可以验证输入或输出的数据是否有效。例如:email是否是有效的email数据类型。

graphql.schema.Coercing中有如下规定:

  • serialise只允许抛出graphql.schema.CoercingSerializeException类型的异常。它意味着value值不能被序列化为合适的数据形式。同时,也必须保证其他类型的runtime exception不会从serialise方法中抛出。另外,返回值必须是非空值。
  • parseValue只允许抛出graphql.schema.CoercingSerializeException类型的异常。它意味着value值不能被解析为合适的input数据形式。同时,也必须保证其他类型的runtime exception不会从parseValue方法中抛出。另外,返回值必须是非空值。
  • parseLiteral只允许抛出graphql.schema.CoercingSerializeException类型的异常。它意味着AST常量值不能被解析为合适的input数据形式。同时,也必须保证其他类型的runtime exception不会从parseLiteral方法中抛出。另外,返回值必须是非空值。

有些人视图依赖runtime exception来实现验证,然后期望它们以graphql error形式输出。但事实并不是这样。在自定义Scalar时,必须遵循Coercing方法的规范,才能使GraphQL - Java正确运行。

示例

下面是一个复杂的Email类新的Scalar实现。

    public static class EmailScalar {

        public static final GraphQLScalarType EMAIL = new GraphQLScalarType("email", "A custom scalar that handles emails", new Coercing() {
            @Override
            public Object serialize(Object dataFetcherResult) {
                return serializeEmail(dataFetcherResult);
            }

            @Override
            public Object parseValue(Object input) {
                return parseEmailFromVariable(input);
            }

            @Override
            public Object parseLiteral(Object input) {
                return parseEmailFromAstLiteral(input);
            }
        });


        private static boolean looksLikeAnEmailAddress(String possibleEmailValue) {
            // ps.  I am not trying to replicate RFC-3696 clearly
            return Pattern.matches("[A-Za-z0-9]@[.*]", possibleEmailValue);
        }

        private static Object serializeEmail(Object dataFetcherResult) {
            String possibleEmailValue = String.valueOf(dataFetcherResult);
            if (looksLikeAnEmailAddress(possibleEmailValue)) {
                return possibleEmailValue;
            } else {
                throw new CoercingSerializeException("Unable to serialize " + possibleEmailValue + " as an email address");
            }
        }

        private static Object parseEmailFromVariable(Object input) {
            if (input instanceof String) {
                String possibleEmailValue = input.toString();
                if (looksLikeAnEmailAddress(possibleEmailValue)) {
                    return possibleEmailValue;
                }
            }
            throw new CoercingParseValueException("Unable to parse variable value " + input + " as an email address");
        }

        private static Object parseEmailFromAstLiteral(Object input) {
            if (input instanceof StringValue) {
                String possibleEmailValue = ((StringValue) input).getValue();
                if (looksLikeAnEmailAddress(possibleEmailValue)) {
                    return possibleEmailValue;
                }
            }
            throw new CoercingParseLiteralException(
                    "Value is not any email address : '" + String.valueOf(input) + "'"
            );
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
GraphQL 是一种用于 API 的查询语言以及一种满足你数据查询的运行时。Java 有很多 GraphQL 的实现框架,其中比较受欢迎的有 graphql-java 和 spring-graphql。 下面是一个使用 graphql-java 的简单例子: 1. 添加依赖 在 Maven 中添加以下依赖: ```xml <dependency> <groupId>com.graphql-java</groupId> <artifactId>graphql-java</artifactId> <version>16.2.0</version> </dependency> ``` 2. 定义 Schema 在 GraphQL 中,Schema 定义了可查询的类型和字段。因此,我们需要定义一个 Schema,并将其转换为 GraphQLSchema 对象。 ```java import graphql.schema.GraphQLObjectType; import graphql.schema.GraphQLSchema; import static graphql.Scalars.*; import static graphql.schema.GraphQLFieldDefinition.*; public class HelloWorldSchema { public static void main(String[] args) { GraphQLObjectType queryType = newObject() .name("helloWorldQuery") .field(newFieldDefinition() .type(GraphQLString) .name("hello") .staticValue("world")) .build(); GraphQLSchema schema = GraphQLSchema.newSchema() .query(queryType) .build(); System.out.println(schema); } } ``` 3. 执行查询 现在我们可以执行一个查询并得到结果了。 ```java import graphql.ExecutionResult; import graphql.GraphQL; public class HelloWorld { public static void main(String[] args) { GraphQLSchema schema = HelloWorldSchema.getSchema(); GraphQL graphQL = GraphQL.newGraphQL(schema).build(); ExecutionResult result = graphQL.execute("{ hello }"); System.out.println(result.getData().toString()); } } ``` 输出结果为: ``` {hello=world} ``` 这就是一个简单的 GraphQL 查询。你可以通过定义更复杂的 Schema 来支持更强大的查询。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值