Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
本文首发于IBMDev社区
Golang以其高效、稳定、简单吸引了大量开发者使用,越来越多公司和云计算平台开始选择Golang作为后端服务开发语言。 Golang对比目前主流的后端开发语言Java具有以下优势:
本文将以打造一个电影网站的后端服务为例,一步步教你如何用Golang开发GraphQL服务,内容涵盖:
搭建出的服务整体架构如图:
在阅读本文前你需要有一定的Golang基础,你可以阅读免费电子书入门。
Golang标准库内置的net/http包能快速实现一个HTTP服务器:
import ( "fmt" "net/http" ) func main() { http.HandleFunc("/hello", func(writer http.ResponseWriter, request *http.Request) { fmt.Fprintf(writer, "Hello, World!") }) http.ListenAndServe(":8080", nil) // HTTP服务监听在8080端口 }
但其功能太基础,要用在实际项目中还需要自己补充大量常用基础功能,例如:
/movie/:id
/static
好在Golang社区中已有多款成熟完善的HTTP框架,例如Gin、Echo等。 Gin和Echo功能相似,但Echo文档更齐全性能更好,因此本文选择Echo作为HTTP框架,接下来详细介绍Echo的用法。
Echo封装的简约但不失灵活,只需以下代码就能快速实现一个高性能HTTP服务:
import ( "net/http" "github.com/labstack/echo/v4" ) func main() { e := echo.New() e.GET("/hello", func(context echo.Context) error { return c.String(http.StatusOK, "Hello, World!") }) e.Start(":8080") // HTTP服务监听在8080端口 }
要实现需要响应JSON也非常简单:
// 响应map类型JSON e.GET("/map", func(context echo.Context) error { return context.JSON(http.StatusOK, map[string]interface{}{"Hello": "World"}) }) // 响应数组类型JSON e.GET("/array", func(context echo.Context) error { return context.JSON(http.StatusOK, []string{"Hello", "World"}) }) // 响应结构体类型JSON type Hi struct { Hello string `json:"Hello"` } e.GET("/struct", func(context echo.Context) error { return context.JSON(http.StatusOK, Hi{ Hello: "World", }) })
如果请求中带有参数,Echo能方便的帮你解析出来:
e.GET("/params/:operationName", func(context echo.Context) error { email := c.QueryParam("email") // 从URL params?email=abc 中提取email字段的值 operationName := c.Param("operationName") // 从URL params/:abc 中提取operationName字段的值 variables := c.FormValue("variables") // 从POST Form请求的body中提取variables字段的值 })
Echo还提供更强大的Bind功能,能根据请求自动的提取结构化的参数,同时还能校验参数是否合法:
// 定义参数的结构 type Params struct { Email string `validate:"required,email"` // 改字段必填,并且是email格式 // 从JSON和Form请求中提取的字段名称是operationName,从URL中提取的字段名称是operation_name OperationName string `json:"operationName" form:"operationName" query:"operation_name"` Variables map[string]interface{} } e.GET("/structParams", func(context echo.Context) (err error) { params:= Params{} // Bind将自动根据请求类型,从URL、Body中提取参数转换为Params struct中定义的结构 err = context.Bind(¶ms) // 如果校验失败,err将非空表示校验失败信息 if err != nil { retuen } })
在获取响应给客户端的数据时可能会发生异常,这时候需要HTTP服务作出响应,Echo的错误处理设计的很优雅:
e.GET("/movie", func(context echo.Context) (err error) { // 获取电影数据,可能发生错误 movie, err := getMovie() // 如果获取电影失败,直接返回错误 if err != nil { // 客户端将收到HTTP 500响应码,内容为:{"message": "err.Error()对应的字符串"} retuen } return context.JSON(http.StatusOK, movie) })
如果你不想返回默认的500错误,例如没有权限,可以自定义错误码:
e.GET("/movie", func(context echo.Context) (err error) { movie, err := getMovie() if err != nil { // 客户端将收到HTTP 401响应码,内容为:{"message": "err.Error()对应的字符串"} retuen echo.NewHTTPError(http.StatusUnauthorized, err.Error()) } })
如果你不想在出错时响应JSON,例如需要响应HTTP,可以自定义错误渲染逻辑:
e.HTTPErrorHandler = func(err error, context echo.Context) { return context.HTML(http.StatusUnauthorized, err.Error()) }
Echo内置了大量实用的中间件,例如:
import ( "github.com/labstack/echo/middleware" ) // 采用Gzip压缩响应后能传输更少的字节,如果的HTTP服务没有在Nginx背后建议开启 e.Use(middleware.Gzip()) // 支持接口跨域请求 e.Use(middleware.CORS()) // 记录请求日志 e.Use(middleware.Logger())
GraphQL作为一种全新的api设计思想,把前端所需要的api用类似图数据结构的方式结构清晰地展现出来,让前端很方便的获取所需要的数据。 GraphQL可用来取代目前用的最多的restful规范,相比于restful有如下优势:
自从FaceBook2012公布了GraphQL规范后,引起了很多大公司和社区关注,逐渐有公司开始使用GraphQL作为API规范。 在Golang社区中也涌现了多个GraphQL服务端框架,例如:
本文将选择第三个graphql-go作为GraphQL服务端框架,接下来介绍如何使用它。
假如我们需要实现一个搜索电影的服务,我们需要先定义接口暴露的Schema
schema { query: Query } type Query { search(offset: Int,size: Int,q: String): [Movie!] # 通过关键字搜索电影 } type Movie { id: Int! title: String! casts: [Cast!]! # 一个电影有多个演员 image: String! } type Cast { id: Int! name: String! image: String! }
客户端在调用接口时只需要发送以下请求:
{ search(q:"你好"){ title image casts{ name } } }
实现根query的取值逻辑:
import ( "net/http" "github.com/gwuhaolin/echo_graphql" "github.com/labstack/echo" "github.com/graph-gophers/graphql-go" ) // 定义筛选参数结构,对应Schema中定义的search方法的参数 type MovieFilter struct { Offset *int32 Size *int32 Q *string } type QueryResolver struct { } // 对应Schema中定义的search方法,如果方法的error不为空,将响应500错误码 func (r *QueryResolver) Search(ctx context.Context, args model.MovieFilter) ([]*MovieResolver, error) { ms, e := db.SearchMovies(args) return WrapMovies(ms), e }
实现获取电影信息的取值逻辑:
type MovieResolver struct { *model.Movie } func (r *MovieResolver) ID() int32 { return r.Movie.ID } func (r *MovieResolver) Title() string { return r.Movie.Title } func (r *MovieResolver) Image() string { return r.Movie.Image } func (r *MovieResolver) Casts() ([]*CastResolver, error) { cs, err := db.Casts(r.Movie.ID) // 把返回的Cast数组包裹为CastResolver数组 return WrapCasts(cs), err } // 把返回的Movie数组包裹为MovieResolver数组 func WrapMovies(movies []*model.Movie) []*MovieResolver { msr := make([]*MovieResolver, 0) for i := range movies { msr = append(msr, &MovieResolver{movies[i]}) } return msr }
演员信息的取值实现逻辑和电影的非常相似就不再复述。
定义的Schema和Golang代码之间有一个很清晰的映射,包括下钻的嵌套字段,如图:
graphql-go暴露了一个Exec函数用于执行GraphQL语句,改函数入参为上下文和请求体返回为获取到的数据,用发如下:
schema := graphql.MustParseSchema(`上面定义的Schema`, QueryResolver{}, graphql.UseFieldResolvers()) data := schema.Exec(context.Request().Context(), params.Query, params.OperationName, params.Variables)
其中Exec的入参都可以通过Echo拿到:
// graphql请求体的标准格式 type Params struct { Query string `json:"query"` OperationName string `json:"operationName"` Variables map[string]interface{} `json:"variables"` } // 在Echo中注册graphql路由 e.Any("/graphql", func(context echo.Context) (err error) { params := Params{} err = context.Bind(¶ms) if err != nil { return } data := schema.Exec(context.Request().Context(), params.Query, params.OperationName, params.Variables) return context.JSON(http.StatusOK, data) })
以上就开发完了一个基于Golang的GraphQL服务。
使用Docker部署服务能抹去大量繁琐易错的手工操作,使用Docker部署的第一步是需要把我们上面开发完的GraphQL服务构建成一个镜像, 为此需要写一个Dockerfile:
FROM golang:latest as builder WORKDIR /app COPY . . RUN go mod download RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o main ./http FROM alpine:latest COPY --from=builder /app/main . EXPOSE 80 EXPOSE 443 CMD ["./main"]
同时你可以定义一个Github Action来自动构建和发布镜像,新增Action配置文件.github/workflows/docker.yml如下:
.github/workflows/docker.yml
name: release on: [push] jobs: dy-server: runs-on: ubuntu-latest steps: - uses: actions/checkout@v1 - name: Docker dy-server release uses: elgohr/Publish-Docker-Github-Action@master with: name: gwuhaolin/projectname/http-server username: gwuhaolin password: ${{ github.token }} registry: docker.pkg.github.com dockerfile: http/Dockerfile workdir: ./
每次你向Github推送代码后都会自动触发构建生产最新的GraphQL服务镜像,有了镜像你可以直接通过docker运行服务:
docker run -d --name http-server -p 80:80 -p 443:443 docker.pkg.github.com/gwuhaolin/projectname/http-server:latest
自从2009年发布Golang到现在,Golang社交已发展的非常成熟,你可以在开源社区找到几乎所有的现成框架。 使用Golang开发出的Graphql服务不仅能支撑高并发量,编译出的产物也非常小, 由于不依赖虚拟机,搭配上Docker带来的自动化部署给开发者节省成本的同时又带来稳定和便利。
虽然Golang能开发出小巧高效的Graphql服务,但可以看出在实现GraphQL取数逻辑那块有大量繁琐重复的工作, 这归咎于Golang语法太过死板无法给框架开发者发挥空间来实现使用更便利的框架,希望后续Golang2能提供更灵活的语法来优化这些不足。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
本文首发于IBMDev社区
Golang以其高效、稳定、简单吸引了大量开发者使用,越来越多公司和云计算平台开始选择Golang作为后端服务开发语言。
Golang对比目前主流的后端开发语言Java具有以下优势:
本文将以打造一个电影网站的后端服务为例,一步步教你如何用Golang开发GraphQL服务,内容涵盖:
搭建出的服务整体架构如图:

HTTP框架
Golang标准库内置的net/http包能快速实现一个HTTP服务器:
但其功能太基础,要用在实际项目中还需要自己补充大量常用基础功能,例如:
/movie/:id
中提取id参数;/static
目录下的所有文件;好在Golang社区中已有多款成熟完善的HTTP框架,例如Gin、Echo等。
Gin和Echo功能相似,但Echo文档更齐全性能更好,因此本文选择Echo作为HTTP框架,接下来详细介绍Echo的用法。
Echo教程
Echo封装的简约但不失灵活,只需以下代码就能快速实现一个高性能HTTP服务:
要实现需要响应JSON也非常简单:
Echo获取请求参数
如果请求中带有参数,Echo能方便的帮你解析出来:
Echo还提供更强大的Bind功能,能根据请求自动的提取结构化的参数,同时还能校验参数是否合法:
Echo错误处理
在获取响应给客户端的数据时可能会发生异常,这时候需要HTTP服务作出响应,Echo的错误处理设计的很优雅:
如果你不想返回默认的500错误,例如没有权限,可以自定义错误码:
如果你不想在出错时响应JSON,例如需要响应HTTP,可以自定义错误渲染逻辑:
Echo常用中间件
Echo内置了大量实用的中间件,例如:
用Golang实现GraphQL
graphql协议介绍
GraphQL作为一种全新的api设计思想,把前端所需要的api用类似图数据结构的方式结构清晰地展现出来,让前端很方便的获取所需要的数据。
GraphQL可用来取代目前用的最多的restful规范,相比于restful有如下优势:
自从FaceBook2012公布了GraphQL规范后,引起了很多大公司和社区关注,逐渐有公司开始使用GraphQL作为API规范。
在Golang社区中也涌现了多个GraphQL服务端框架,例如:
本文将选择第三个graphql-go作为GraphQL服务端框架,接下来介绍如何使用它。
定义GraphQL Schema
假如我们需要实现一个搜索电影的服务,我们需要先定义接口暴露的Schema
客户端在调用接口时只需要发送以下请求:
定义取值逻辑
实现根query的取值逻辑:
实现获取电影信息的取值逻辑:
演员信息的取值实现逻辑和电影的非常相似就不再复述。
定义的Schema和Golang代码之间有一个很清晰的映射,包括下钻的嵌套字段,如图:

打通Echo和graphql-go
graphql-go暴露了一个Exec函数用于执行GraphQL语句,改函数入参为上下文和请求体返回为获取到的数据,用发如下:
其中Exec的入参都可以通过Echo拿到:
以上就开发完了一个基于Golang的GraphQL服务。
使用Docker部署GraphQL服务
使用Docker部署服务能抹去大量繁琐易错的手工操作,使用Docker部署的第一步是需要把我们上面开发完的GraphQL服务构建成一个镜像,
为此需要写一个Dockerfile:
同时你可以定义一个Github Action来自动构建和发布镜像,新增Action配置文件
.github/workflows/docker.yml
如下:每次你向Github推送代码后都会自动触发构建生产最新的GraphQL服务镜像,有了镜像你可以直接通过docker运行服务:
总结
自从2009年发布Golang到现在,Golang社交已发展的非常成熟,你可以在开源社区找到几乎所有的现成框架。
使用Golang开发出的Graphql服务不仅能支撑高并发量,编译出的产物也非常小,
由于不依赖虚拟机,搭配上Docker带来的自动化部署给开发者节省成本的同时又带来稳定和便利。
虽然Golang能开发出小巧高效的Graphql服务,但可以看出在实现GraphQL取数逻辑那块有大量繁琐重复的工作,
这归咎于Golang语法太过死板无法给框架开发者发挥空间来实现使用更便利的框架,希望后续Golang2能提供更灵活的语法来优化这些不足。
The text was updated successfully, but these errors were encountered: