上一篇咱們實現(xiàn)了幾乎所有的數(shù)據(jù)庫訪問代碼。這一次咱們進一步實現(xiàn) GraphQL 接口封裝。
一、GraphqQL 模式建立
- 基礎(chǔ)模式:
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",
})
- 基礎(chǔ)維度模式:
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",
})
- 生態(tài)模式(例如:評論):
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構(gòu)建,以維度為例(其他的都類似):
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("參數(shù)解析失敗")
}
err = json.Unmarshal([]byte(content), &condGetDetails)
if err != nil {
return nil, errors.New("參數(shù)解析失敗")
}
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
},
}
- 每個接口訪問時,除了接口必要的參數(shù)數(shù)據(jù)之外,還附帶上額外的用戶數(shù)據(jù)(
id和nickname),方便以后的審計 -
content是接口必要的參數(shù)數(shù)據(jù),使用的是前面的文章中設(shè)計好的數(shù)據(jù)結(jié)構(gòu) - 博客系統(tǒng)的所有接口參數(shù)盡量保持一致——這是一個能簡化邏輯的約定。
- GraphQL 的 endpoint 接入到 Gin 框架內(nèi):
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 路由相關(guān)代碼實現(xiàn)。