zoukankan      html  css  js  c++  java
  • GraphQL Java

    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) + "'"
                );
            }
        }
    
  • 相关阅读:
    解决VsCode中Go插件依赖安装失败问题
    C# httpclient获取cookies实现模拟web登录
    C#中调用HttpWebRequest类中Get/Post请求无故失效的诡异问题
    VisualSVN 5.1.7破译License Key
    AutoResetEvent类的使用
    26种设计模式之单例模式
    WPF的一些感悟
    vim 常用指令
    myeclipse 的.jsp文件中的<option>无法使用
    flume部署问题解决
  • 原文地址:https://www.cnblogs.com/pku-liuqiang/p/11528040.html
Copyright © 2011-2022 走看看