上一篇咱们实现了几乎所有的数据库访问代码。这一次咱们进一步实现 GraphQL 接口封装。
一、GraphqQL 模式建立
- 基础模式:
var baseType = graphql.NewObject(graphql.ObjectConfig{
Name: "Base",
Fields: graphql.Fields{
"id": &graphql.Field{Type: graphql.ID},
"created_at": &graphql.Field{Type: graphql.DateTime},
"updated_at": &graphql.Field{Type: graphql.DateTime},
"deleted_at": &graphql.Field{Type: graphql.DateTime},
},
Description: "baseType",
})
- 基础维度模式:
var baseDimensionType = graphql.NewObject(graphql.ObjectConfig{
Name: "BaseDimension",
Fields: graphql.Fields{
"name": &graphql.Field{Type: graphql.String},
"category": &graphql.Field{Type: graphql.String},
"content": &graphql.Field{Type: graphql.String},
"tag": &graphql.Field{Type: graphql.String},
},
Description: "baseDimensionType",
})
- 账号模式:
var userType = graphql.NewObject(graphql.ObjectConfig{
Name: "User",
Fields: graphql.Fields{
"account": &graphql.Field{Type: graphql.String},
"password": &graphql.Field{Type: graphql.String},
"time_account_change_latest": &graphql.Field{Type: graphql.DateTime},
"time_login_one": &graphql.Field{Type: graphql.DateTime},
"time_login_second": &graphql.Field{Type: graphql.DateTime},
"base": &graphql.Field{Type: baseType},
"dimension_readings": &graphql.Field{Type: graphql.NewList(dimensionReadingType)},
"dimension_writings": &graphql.Field{Type: graphql.NewList(dimensionWritingType)},
"dimension_photos": &graphql.Field{Type: graphql.NewList(dimensionPhotoType)},
"eco_comments": &graphql.Field{Type: graphql.NewList(ecoCommentType)},
"system_ads": &graphql.Field{Type: graphql.NewList(systemAdType)},
"bind_profiles": &graphql.Field{Type: graphql.NewList(bindProfileType)},
},
Description: "userType",
})
- 维度模式(例如:阅读金句):
var dimensionReadingType = graphql.NewObject(graphql.ObjectConfig{
Name: "DimensionReading",
Fields: graphql.Fields{
"author": &graphql.Field{Type: graphql.String},
"location": &graphql.Field{Type: graphql.String},
"base_dimension": &graphql.Field{Type: baseDimensionType},
"eco_comments": &graphql.Field{Type: graphql.NewList(ecoCommentType)},
"users": &graphql.Field{Type: graphql.NewList(userType)},
},
Description: "dimensionReadingType",
})
- 生态模式(例如:评论):
var ecoCommentType = graphql.NewObject(graphql.ObjectConfig{
Name: "EcoComment",
Fields: graphql.Fields{
"data": &graphql.Field{Type: graphql.String},
"is_published": &graphql.Field{Type: graphql.Boolean},
"base": &graphql.Field{Type: baseType},
},
Description: "ecoCommentType",
})
二、GraphQL 端点(Endpoint)建立
- Endpoint构建,以维度为例(其他的都类似):
var EndpointGetDimensionReading = &graphql.Field{
Type: responseDimensionReadingType,
Args: graphql.FieldConfigArgument{
"from_id": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
"from_nickname": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
"content": &graphql.ArgumentConfig{
Type: graphql.NewNonNull(graphql.String),
Description: "query by cond",
},
},
Resolve: func(p graphql.ResolveParams) (i interface{}, err error) {
var entities []DimensionReading
var count int64
content, contentOk := p.Args["content"].(string)
fromId, fromIdOk := p.Args["from_id"].(string)
fromNickname, fromNicknameOK := p.Args["from_nickname"].(string)
if !contentOk || !fromIdOk || !fromNicknameOK || fromId == "" || fromNickname == "" || content == "" {
return nil, errors.New("required id,name,content")
}
var condGetDetails CondGetDetails
if !contentOk {
return nil, errors.New("参数解析失败")
}
err = json.Unmarshal([]byte(content), &condGetDetails)
if err != nil {
return nil, errors.New("参数解析失败")
}
result, err := GetEntities(condGetDetails)
if err != nil {
return nil, err
}
err = result.Preload(clause.Associations).Find(&entities).Count(&count).Error
return ResponseCommon{
Code: 200,
Content: entities,
Count: count,
Msg: Message{
Success: "success",
},
}, err
},
}
- 每个接口访问时,除了接口必要的参数数据之外,还附带上额外的用户数据(
id
和nickname
),方便以后的审计 -
content
是接口必要的参数数据,使用的是前面的文章中设计好的数据结构 - 博客系统的所有接口参数尽量保持一致——这是一个能简化逻辑的约定。
- GraphQL 的 endpoint 接入到 Gin 框架内:
var queryType = graphql.NewObject(graphql.ObjectConfig{
Name: "Query",
Fields: graphql.Fields{
"dimensionReading": EndpointGetDimensionReading, //获取参展项目列表
},
})
var Schema, _ = graphql.NewSchema(graphql.SchemaConfig{
Query: queryType,
//Mutation: mutationType,
})
func ExecuteQuery(schema graphql.Schema, query string, variables map[string]interface{}, operationName string) *graphql.Result {
result := graphql.Do(graphql.Params{
Schema: schema,
RequestString: query,
VariableValues: variables,
OperationName: operationName,
})
if len(result.Errors) > 0 {
log.Printf("errors:%s", result.Errors)
}
return result
}
- Gin 框架与 GraphQL 的中间连接代码
func RouterDimension(router *gin.Engine) (interface{}, error) {
routerDimension := router.Group("/blog/x")
{
routerDimension.POST("/v1", func(c *gin.Context) {
var query Query
err := c.BindJSON(&query)
if err != nil {
log.Println(err)
c.JSON(http.StatusOK, err)
return
}
result := models.ExecuteQuery(models.Schema, query.Query, query.Variables, query.OperationName) // 此处连接GraphQL
c.JSON(http.StatusOK, result)
})
}
return routerDimension, nil
}
- Gin 路由相关代码实现。