教程集 www.jiaochengji.com
教程集 >  Golang编程  >  golang教程  >  正文 GraphQl与Golang

GraphQl与Golang

发布时间:2021-05-25   编辑:jiaochengji.com
教程集为您提供GraphQl与Golang等资源,欢迎您收藏本站,我们将为您提供最新的GraphQl与Golang资源

文章目录

    • REST与GraphQL
    • 为什么是GraphQL
    • Hello World
      • 造轮子
      • 别人家的轮子
      • 小结
    • 劳动改造
    • 对象与字段
    • 更多类型
      • 列表类型
      • 枚举类型
      • ID类型
      • 接口类型
      • 联合类型
    • 参数
      • 高级查询:变量
    • 变更
    • 输入类型

REST与GraphQL

如果你还不知道什么是GraphQL,那么,请移驾☞此处。

什么实API呢?API的全称是Application Programming Interface,翻译过来就是应用程序编程接口。在网络编程中,API决定了客户端如何从服务器获取数据,或者说API是客户端从服务器获取数据的方式,REST和GraphQL就是这样的方式。

REST和GraphQL本身并不是API,他们是用来修饰API的,也就是说,他们是两种不同的API设计规范,或者说风格。那么他们之间有什么区别呢?

如果你看过爱情公寓的话,有一集中一菲和曾小贤、关谷、子乔打赌。一菲问赌注是什么,子乔说,要是输了就把张伟输给你。一菲很奇怪,我要张伟干什么?这时子乔说了一句很经典的话:和我们赌,不是看你要什么,而是看我们有什么!如果非要形容REST风格的话,我想子乔的这句话应该是最贴切的了。

REST API为客户端提供了一些列端点,每个端点返回特定的数据结构。客户端只能根据自己需要的数据在这些入口点中选择,如果想获得新的结构化数据,就不得不跟服务器商量:嗨,哥们,再提供个API呗。然后等着服务器端不情愿的答复:好了,知道了。

如果说REST是捆绑消费的话,那么GraphQL就是自由消费。假如你的洗发水用完了,你想去超市买瓶洗发水。

你说:我要一瓶洗发水。

REST会说:行,买洗发水送洁厕灵,打八折。

你说:我不要洁厕灵,我就要洗发水。

REST会说:不行,这是活动规则,不要洁厕灵,洗发水就不卖给你。

你:。。。what ?

一脸懵逼的你很想反抗,于是带着洗发水和洁厕灵回家了,不得不接受这惨淡的现实。现在,让我们换成GraphQL再买一次洗发水。

你说:我要一瓶洗发水。

GraphQL会说:行,给你。

就是这么简单,你要什么,GraphQL就能给你什么。当然也不能超出GraphQL所能提供的范围,要是你想买鸽子蛋那么大的钻石,人家上哪儿偷去?

GraphQL只公开单个端点,支持客户端进行声明性数据提取。也就是客户端可以告诉服务器我想要什么数据,然后服务器就把这些数据返回给客户端。

GraphQL被称为是API查询语言,由Facebook实践和开源。服务器公开的单个端点就是一个一个的API,GraphQL提交查询的时候,就像是在查询这些API一样。

如果REST是在以命令的方式要求服务器返回数据,那么GraphQL就是在向服务器描述数据。

使用REST API获取数据时,需要经过四个步骤。

  • 构造并发送HTTP请求
  • 接收和响应服务器响应
  • 在本地存储数据
  • 在UI中显示数据

而使用GraphQL API时只需要两步。

  • 描述数据
  • 显示数据

GraphQL最初用于React语言,但是它可以由任何语言实现。幸运的是,已有Golang的爱好者对此作出了贡献,目前在Github上有三个包可用。如下:

graphql

graphql-go

magellan

其中第一个包显得有些笨拙,它大量使用了空接口来实现。第二个包和第三个包非常类似,使用了静态代码分析来实现。它们之间的另一个重大区别是,第二个包和第三个包支持SDL(Schema Definition Language:模式定义语言),而第一个包不支持。更多关于SDL的介绍会在后面展开。

我主要使用第二个包,第一个包会用一个例子做一个入门介绍。

使用GraphQL可以带来哪些好处呢?

  1. 减少需要通过网络传输的数据量,提高数据加载效率,因为买洗发水不会送你洁厕灵。

  2. 前端差异透明化。如果我和你一起去超市买东西,我要买洗发水和沐浴露,而你要买沐浴露和洗面奶,而最终的结果可能是REST将洗发水、沐浴露和洗面奶捆绑销售,我买了不需要的洗面奶,你买了不需要的洗发水。但GraphQL会将这三样东西分开卖,这样,我和你可以根据自己的需要选择商品,这样客户端的差异在服务端就透明化了。

  3. 灵活、快速开发。当沐浴露和洗发水的组合卖的热火朝天的时候,突然有一天你要买沐浴露和洗面奶,如果将洗面奶加到洗发水和沐浴露的组合中,又可能失去之前的客户,于是REST就不得不再推出一款洗面奶和沐浴露的组合。但是如果是GraphQL,完全不用对API做出任何改动,由客户端改变选择就行了。

总结起来就是灵活、高效。

为什么是GraphQL

GraphQL的出现号称是为了解决REST存在的问题,知道REST的问题,就相当于是知道了GraphQL的优势。

问题1:过度和不足

过度意味着下载了多于的数据,就好比买洗发水送的洁厕灵。不足意味着靠一个API无法得到所需的所有数据,由此就会引发n 1问题,也就是说客户端必须发起额外的请求才能获得所需数据。这就好比如果我需要的是洗发水和洗面奶,那么我就不得不买两个套餐。

问题2:限制前段迭代速度

如果后端定下来了,那么前端获取数据的方式也就确定了。如果这时前端需要改动,那么很有可能连同后端也需要作出适当的修改。而在GraphQL中,前端的改动几乎不会要求后端作出任何改动。

GraphQL还带来了另外两个优势。

优势1:后端分析

超市每个月要做的事就是分析商品的销售形式,卖的火的要增加库存,并把没人买的商品及时下架。GraphQL允许你对请求数据做细粒度的分析,了解哪个客户端对那些数据感兴趣,并弃用任何客户端都不再请求的字段。如果洁厕灵和洗发水绑定在了一起,而洁厕林又没人买的话,那么它只能长期占用库存。

此外,还可以对请求进行性能监视。GraphQL通过解析器函数返回客户端请求的数据,监测这些解析器的性能可以帮助你找到系统新能的瓶颈。

优势2:前后端独立开发

这一点依赖于GraphQL的Schema和类型系统,Schema和类型系统是GraphQL的发明。Schema是用SDL编写的,它是服务器和客户端之间的契约。Schema定义了客户端访问数据的方式,就像是一份商品目录清单,告诉你在这个超市中你可以买到哪些东西。类型系统则定义了API的功能,具体来说是定义了API应该返回什么样的数据。比如说,超市应该卖洗发水,可以是海飞丝,也可以是飘柔,还可以是蒂花之秀,但不能是舒肤佳,因为类型系统规定了必须是洗发水。

一旦Schema定义好了,前端和后台就可以各干各的了,因为后台知道自己该提供什么数据,前端也知道自己可以获取哪些数据。就好比一旦商品目录清单确定了,采购和导购员就可以各自干活了。

Hello World

准备工作:下载github.com/graph-gophers/graphql-go包。

go get github.com/graph-gophers/graphql-go

下面的代码可能会让你一头雾水,但是不要问为什么,因为必须这么写,所有你心中的疑惑,我都会在后面解开。

首先我们需要写一段Schema代码,用SDL语言写。

var s = `
        schema {
            query: Query
        }
        type Query {
            hello: String!
        }
`

因为是在Golang中,所以这段SDL语言写的代码只能以字符串的形式存在。schema定义的就是GraphQL的Schema,它是一份清单,定义客户端可以进行的操作,这里是query,表示客户端可以进行查询操作。type对应的就是类型系统,它定义了Query对象,这个对象有一个String类型的字段,叫做helloString后面的感叹号表示该字段为非空字段,也就是说当客户端查询这个字段时,服务器一定会返回一个值。

Schema只是一份契约,至于契约的执行,也就是如何提供数据以及提供什么样的数据还需要Go的支持。

type query struct{}
func (_ *query) Hello() string {
    return "hello world"
}

这里的query结构体就是解析器,它的方法Hello就是解析器函数。

下一步缔结契约,既然双方都已准备好,签了字契约才算生效。

var schema = graphql.MustParseSchema(s, &query{})

还差最后一步,为GraphQL API开通Http服务,直接用Golang提供的Http服务就行了。不过在此之前我们需要导入github.com/graph-gophers/graphql-go/relay包,用它提供的功能将Schema转换成Golang处理器。

func main() {
    h := relay.Handler{schema}
    http.Handle("/hello", &h)
    http.ListenAndServe(":8080", nil)
}

服务端的代码就编写完了,接下来是怎么玩的问题了。先将程序运行起来,现在是万事具备,只欠东风。

如果有安装curl的话,可以打开powershell,输入如下命令。

curl -X POST -d '{\"query\":\"{hello}\"}' localhost:8080/hello

如果一切正常,将会得到下面的输出。

{"data":{"hello":"hello world"}}

这是一段json字符串,表明查询结果是hello world

为什么要用这种方式?因为relay提供的处理器是通过解析Body中的json串来获得客户端的查询请求的,使用curl是最简单的测试方式了,后面我会用一种更加好看的方式。

将curl POST的内容展开就是下面的内容。

{
    "query": "{hello}"
}

其实真正扮演GraphQL查询角色的是{hello},这样写也是语法使然。而"query"的存在只是为了处理器能正确的提取出后面的{hello}。当你买洗发水的时候,你需要跟服务员说:我要一瓶洗发水,飘柔的。你说的这些话就如同这里的{hello},它告诉服务器我要查询hello字段。

通过这个例子,我们已经了解到了GraphQL两个重要的内容:Schema和查询。更多的语法会在后面介绍,我们的主要工作也在于编写这两部分的内容。

附:完整代码☟

package main

import (
	"net/http"
	graphql "github.com/graph-gophers/graphql-go"
    "github.com/graph-gophers/graphql-go/relay"
)

type query struct{}

func (_ *query) Hello() string {
	return "hello world"
}

var s = `
		schema {
			query: Query
		}
		type Query {
			hello: String!
		}
	`
var schema = graphql.MustParseSchema(s, &query{})

func main() {
	h := relay.Handler{schema}
	http.Handle("/query", &h)
	http.ListenAndServe(":8080", nil)
}

作为对比,我们来看看使用github.com/graphql-go/graphql包如何写出Hello World。

首先还是需要下载这个包。

go get github.com/graphql-go/graphql

套路还是一样的,需要编写Schema。不同的是这次草拟契约和签订契约不再是两件事,而是变成了一件事。这就意味着,我们不再使用SDL语法以字符串的形式编写Schema了,而是要用Golang语法来编写。

首先需要写类型系统,与前边对应的是type Query{hello:String!}这部分代码。

var queryType = graphql.NewObject(graphql.ObjectConfig {
    Name: "Query",
    Fields: graphql.Fields{
        "hello": &graphql.Field{
            Type: graphql.String,
            Resolve: func(p graphql.ResolveParams) (interface{}, err) {
                return "hello world", nil
            },
        },
    },
})

这么长一段就说明了一件事:有一个叫做Query的GraphQL对象,它有一个叫hello的字段,类型是String,而Resolve函数定义了该字段的解析器函数,也就是说当客户端查询hello字段时,就会调用这个函数。

然后是定义Schema,对应前面schema{query:Query}这段代码。

var Schema, _ = graphql.NewSchema(graphql.SchemaConfig{Query: queryType})

最后一步是把Schema挂到Http服务上,不幸的是,这个包没有提供直接将Schema转为Handler的功能。然而万幸的是,有另一个包提供了这一功能。不过俗话说的好,自己动手,丰衣足食。我想说的是,我们不妨先自己造个轮子,然后在用用别人的轮子。

造轮子

graphql包提供了Do函数来执行查询,需要Schema和查询字符串作为参数,我们只需要在处理器中调用这个函数就可以完成GraphQL查询了。

func hello(w http.ResponseWriter, r *http.Request) {
    result := graphql.Do(graphql.Params{
        Schema: schema,
        RequestString: r.URL.Query().Get("query"),
    })
    if len(result.Errors) > 0 {
        fmt.Fprintf(w, "Wrong result, unexpected errors: %v", result.Errors)
        return
    }
    json.NewEncoder(w).Encode(result)
}

最后main函数只需要像普通的Http程序那样开启Http父服务就行了。

func main() {
    http.HandleFunc("/hello", hello)
    http.ListenAndServe(":8080", nil)
}

迫不及待想看看结果了吗?在curl中输入curl localhost:8080/hello?'query=\{hello\}',回车之后就能看到服务器发回的数据了,和第一个例子一模一样。此外,也可以在浏览器中输入localhost:8080/hello?query={hello},也能得到相同的结果。

完整代码:

package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"github.com/graphql-go/graphql"
)

var queryType = graphql.NewObject(
	graphql.ObjectConfig{
		Name: "Query",
		Fields: graphql.Fields{
			"hello": &graphql.Field{
				Type: graphql.String,
				Resolve: func(p graphql.ResolveParams) (interface{}, error) {
					return "hello world", nil
				},
			},
		},
	},
)

var schema, _ = graphql.NewSchema(graphql.SchemaConfig{Query: queryType})

func hello(w http.ResponseWriter, r *http.Request) {
    result := graphql.Do(graphql.Params{
        Schema: schema,
        RequestString: r.URL.Query().Get("query"),
    })
    if len(result.Errors) > 0 {
        fmt.Fprintf(w, "Wrong result, unexpected errors: %v", result.Errors)
        return
    }
    json.NewEncoder(w).Encode(result)
}

func main() {
	http.HandleFunc("/hello", hello)
	http.ListenAndServe(":8080", nil)
}

别人家的轮子

不管怎么说自己造的轮子还是有点简陋,好在github.com/graphql-go/handler包提供了GraphQL转Http处理器的支持。让我们下载过来试一试。

go get github.com/graphql-go/handler

现在我们不用自己写处理器函数了,mian函数需要做一点修改。

func main() {
    h := handler.New(&handler.Config{
        Schema: &Schema,
        pretty: true,
        GraphiQL: true,
    })
    http.Handle("/hello", h)
    http.ListenAndServe(":8080", nil)
}

在浏览器中输入localhost:8080/hello,哇喔~ 有没有很酷。

左边区域用于输入查询,点击长得很像播放的那么按钮,右边就会显示查询结果。

这样就方便和直观多了,不得不承认,别人造的轮子就是比自己造的好,果然都是别人的的轮子。

完整代码:

package main

import (
	"net/http"
	"github.com/graphql-go/graphql"
	"github.com/graphql-go/handler"
)

var queryType = graphql.NewObject(graphql.ObjectConfig{
	Name: "Query",
	Fields: graphql.Fields{
		"hello": &graphql.Field{
			Type: graphql.String,
			Resolve: func(p graphql.ResolveParams) (interface{}, error) {
				return "hello world", nil
			},
		},
	},
})

var Schema, _ = graphql.NewSchema(graphql.SchemaConfig{Query: queryType})

func main() {
	h := handler.New(&handler.Config{
		Schema:   &Schema,
		Pretty:   true,
		GraphiQL: true,
	})
	http.Handle("/hello", h)
	http.ListenAndServe(":8080", nil)
}

小结

对比这两个例子,graphql包大量使用了结构体嵌套来模仿SDL,虽然结构清晰,不需要额外编写SDL代码,但是代码量却是有增无减,一旦功能复杂起来,代码结构会不忍直视。唯一的优势是有配套的处理器转换包,不过我们也可以为graphql-go包写一套一模一样的处理器转换包。反观graphql-go包,代码就简洁多了,而且SDL代码可以让人一眼就看出程序的功能。

综上所述,换而言之,后面的学习中,我们只会用graphql-go这个包了。就决定是你了,去吧,皮卡丘~

劳动改造

为了在使用github.com/graph-gophers/graphql-go包时也有漂亮的轮子可以用,我们需要fork一下github.com/graphql-go/handler包,并做一点改造。

需要改动的地方不是很多,并且你可以根据自己的习惯,做出个性化的修改和定制。我属于有点强迫症的人,所以做了一些定制,怎么改其实无所谓,只要你自己喜欢而且能用就行。

为什么要在这里强调这一点呢?因为后面的学习中,我就全部改用自己改造的包来生成处理器了,这并不是最重要的部分,只是一种可以更加清晰和直观的看到代码效果的方式。

如果你跳过了这一章,或者因为它不重要而放弃。那么在后面你可能会看到一些奇怪的代码,在你的机器上编译器会告诉你找不到某些函数,或者在你那里看不到和我一样的效果。对于初学者,这是很糟糕的,我不希望因为这些本不重要的事而阻碍了你学习的进度和热情。我也是从菜鸟走过来的,虽然现在任然很菜,我要说的是我能体会初学者的心情,因此本章的内容务必认真完成,然后再往下走。

首先在你的GOPATH下新建一个目录GraphQL-Handler,然后将handler文件夹拷贝进去。这个过程完全可以自定义。我的目录是这样的,如果你建的目录和我的不一样,记得导入handler包的时候换成你自己的目录就行了。

github.com/graphql-go/handler包的内容并不多,我们只需要改其中两个脚本就可以使用了。

至于测试文件,它们并不会影响使用,甚至你可以将它们删除。但是写测试文件是一个好习惯,并且是必要的,这里暂时不修改它们只是为了不增加不必要的复杂度,因为我们并不打算发布它。

首先,在handler.go文件中,我们需要添加一个代表查询的结构体和两个执行查询的方法。

然后再增加一个生成处理器的函数。

最后需要修改handler.go中的ContextHandler函数。

handler.go就修改完了,再次声明,这只是一种符合我习惯的修改方式,不是必须这么做。

graphigl.go文件的修改就简单多了,只需要修改renderGraphiQL函数就可以了。

修改完以后可以用前面的例子测试一下,在[Hello World](#Hello World)的第一个例子中,首先导入我们修改过的handler包。

import “GraphQL-Handler/handler"

然后将main函数修改如下。

func main() {
    h := handler.HttpHandler(schema, true, true)
	http.Handle("/hello", h)
	log.Fatal(http.ListenAndServe(":8080", nil))
}

在浏览器中输入localhost:8080/hello回车,看到如下图的界面就OK了。

对象与字段

编写Schema非常像是在定义结构体,还是以[Hello World](#Hello World)中的例子来说明。

schema {
    query: Query
}
type Query {
    hello: String!
}

这是最简单的Schema了,schema关键字用来定义一个Schema;query是它的字段;Queryquery字段的类型。但是这个类型并不是GraphQL的类型,而是我们自定义的对象类型,紧接着我们就用type关键字解释了Query类型。

在GraphQL中用type ObjectName{...}来定义对象,对象也是类型。用Field:Type来为对象定义字段,这里的Type既可以是GraphQL的标量类型,也可以是自定义的对象类型,甚至枚举类型,这些类型都会在后面介绍。

schema的定义十分刻板,它只能有三个字段:querymutationsubscription。这三个字段分别代表查询、修改和订阅。对比REST,query就相当于GET请求,mutation就相当于POSTDELETE请求。因为现在只涉及查询,mutationsubscription会在后面专门介绍。

query必须是一个对象类型。必须是Query吗?当然不是,可以是任意名字,只要是一个对象类型,这样写只是一种习惯,并不是语法约束。

关键字type用来定义对象,一个对象可以拥有若干字段。这里Query对象拥有hello字段,类型是String类型。GraphQL中的标量类型还包括IntFloatBoolean。类型后面加上!就表示该字段为非空字段,换句话说,当你查询这类字段时,一定会给你返回一个值。就好比食盐是超市必须卖的商品,当你去超市买食盐的时候,他绝不会跟你说没有。

那么Schema和查询有什么关系呢?在上一个例子中,我们测试程序时是输入下面的内容进行查询的。

{
    hello
}

其实在大括号中输入的内容就是Query对象的字段。那么这和schema有什么关系呢?其实上面的查询是简写版,我们省略了query关键字,完整的查询应该像下面这样。

query {
    hello
}

QraphQL允许我们在查询时省略query关键字,以大括号开头的默认就是查询。现在插叙和Schema之间的关系就非常明朗了,我们查询query字段的hello字段,其实就是Query对象的hello字段。

编写Schema的工作也就是在定义对象和字段,知道了如何定义字段和对象,我们就可以来实现一个复杂点的例子了,并以此填上一些坑。

####开工

查询一定是以数据为基础的,现在假设我们有一个film结构体,定义如下。

type film struct {
    Name    string  //电影名
    Country string  //国家
    Year    int32   //年份
    Runtime int32   //时长(分钟)
    Color   bool    //是否为彩色
    Score   float64 //评分
}

为了客户端能够查询这个结构体的数据,我们需要编写一个Schema。按照前面例子的经验,我们可以这样编写。

var s = `
		schema {
    		query: Query
		}
		type Query {
    		name   : String!
    		country: String!
    		year   : Int!
    		runtime: Int!
    		color  : Boolean
    		score  : Float!
		}
`

这样写是可以的,但不是最好的。这样会带来两个问题:一是结构混乱。因为写在Query对象中的字段明显不是属于它的,而是属于Film对象的。二是使Query对象变得非常庞大。如果有几十上百个结构体数据,而所有字段都在写在Query对象中,可想而知,Query对象将变得多么复杂和庞大。无论是哪一点,无疑都会增加代码维护的难度。因此,我们再定义一个Film对象,分担Query对象的压力。

var s = `
		schema {
            query: Query
        }
        type Query {
            film: Film
        }
        type Film {
            name   : String!
            country: String!
            year   : Int!
            runtime: Int!
            color  : Boolean
            score  : Float!
        }
`

这样看起来的清晰明了多了。不过请特别注意一点,只有color字段的类型Boolean后面没有加!,加不加!并无强制规定,只是先记住这里的Boolean后面没有!,后面用得上。

第一步已经完成了,下面就是要得到Golang支持,也就是为每个字段编写解析器函数。再次回顾一下,Schema的每个对象对应着一个解析器,也就是Golang的结构体;每个字段对应一个解析器函数,也就是Golang结构体的一个方法。需要说明的是,解析器函数的名字必须和Schema中的字段名字一样,一般习惯是字段名全小写,解析器函数名就是首字母大写的字段名。一定一定,不要随便写解析器函数的名字。

这一次,我们需要从下至上编写解析器函数。为什么?因为Query对象的film字段并不是一个标量类型,可以直接解析,它是Film对象类型,意味着它也对应着一个解析器。反观Film对象的字段都是标量类型,直接对应解析器函数。

type filmResolver struct {
    f *film
}

func (r *filmResolver) Name() string {
	return r.f.Name
}

func (r *filmResolver) Country() string {
	return r.f.Country
}

func (r *filmResolver) Year() int32 {
	return r.f.Year
}

func (r *filmResolver) Runtime() int32 {
	return r.f.Runtime
}

func (r *filmResolver) Color() *bool {
	return &r.f.Color
}

func (r *filmResolver) Score() float64 {
	return r.f.Score
}

如果你观察力敏锐的话,应该注意到Color函数返回的是*bool类型,而其他方法都是返回的值类型。如果注意到了这一点,先记住。

Query对象的film字段是Film对象类型,而这个对象有自己的解析器,因此,film字段的解析器函数只需要返回Film对象的解析器就可以了。问题在于,filmResolver需要一个film类型的字段,这就是数据。查询是基于数据的,因此在编写Query对象的解析器之前,还需要伪造一份数据。

var Hidden_Man = &film{
	Name:    "邪不压正",
	Country: "China",
	Year:    2018,
	Runtime: 137,
	Color:   true,
	Score:   7.1,
}

目前数据是我们自己伪造的,实际应用中应该是查询数据库得到的,有了数据,就可可以编写Query对象的解析器了。

type Resolver struct{}

func (r *Resolver) Film() *filmResolver {
    return &filmResolver{Hidden_Man}
}

双方都已准备就绪,是时候缔结契约了。

var schema = graphql.MustParseSchema(s, &Resolver{})

最后是main函数。这次我们用自己改造过的轮子来生成处理器。所以要记得先导入上一章改造过的handler包。

import "GraphQL-Handler/handler"

func main() {
	h := handler.HttpHandler(schema)
	http.Handle("/film", h)
	http.ListenAndServe(":8080", nil)
}

####安排~

运行程序,在浏览器中输入localhost:8080/film回车,让我们来试试查询Hidden_Man的各个字段。需要注意的是,现在film不是一个可直接查询的字段,它本身也是一个解析器,只有那些标量类型的字段才能直接查询,所以现在查询应该这样写。

{
    film {
        name
        country
        Year
        Runtime
        score
        Color
    }
}

查询结果应该如下图。

在结束本章之前,还有几个问题需要澄清一下,前面有两个地方让大家先记住。一是在Schema中定义Film对象时只有Color字段的类型Boolean后面没有加!;二是Color字段的解析器函数返回的是*bool类型。

现在我要提出这样四个问题。

  1. Film对象的其他字段的类型后可不可以也不加! ?
  2. Color字段的解析器函数可不可以返回bool类型的值?
  3. Year字段和Runtime字段的解析器函数可不可以返回int类型?
  4. Score字段的解析器函数可不可以返回float32类型?

以上四个问题的答案都是否定的,不信可以亲自试一试。

这四个问题揭示了三个实现上的规则,是只有用Golang时才会有的规则,不是GraphQL本身的规则。

  1. 如果Schema中对象的某个字段可以为空,也就是其类型后没有!的话,那么它的解析器函数必须返回指针类型,而不能是值类型。
  2. GraphQL中的Int类型必须对应Golang中的int32类型。
  3. GraphQL中的Float类型必须对应Golang中的float64类型。

收工,本章完~

附:完整代码☟

package main

import (
	"GraphQL-Handler/handler"
	"net/http"
	graphql "github.com/graph-gophers/graphql-go"
)
// Schema
var s = `
		schema {
    		query: Query
		}
		type Query {
    		name   : String!
    		country: String!
    		year   : Int!
    		runtime: Int!
    		color  : Boolean
    		score  : Float!
		}
`

type film struct {
    Name    string  //电影名
    Country string  //国家
    Year    int32   //年份
    Runtime int32   //时长(分钟)
    Color   bool    //是否为彩色
    Score   float64 //评分
}

var Hidden_Man = &film{
	Name:    "邪不压正",
	Country: "China",
	Year:    2018,
	Runtime: 137,
	Color:   true,
	Score:   7.1,
}
//Query解析器
type Resolver struct{}

func (r *Resolver) Film() *filmResolver {
	return &filmResolver{Hidden_Man}
}

type filmResolver struct {
	f *film
}
//Film解析器
func (r

您可能感兴趣的文章:
GraphQl与Golang
JWT Token认证
golang基础教程
golang timewheel 时间轮定时器设计与实现
golang的调度总结
golang key map 所有_golang推断map中指定key是不是存在_后端开发
golang会取代php吗
Golang环境安装&IDEA开发Golang
golang中的nil
ubuntu下安装golang(转)

[关闭]
~ ~