532.【博客開發(fā)】服務端接口實現(二)

上一篇咱們實現了幾乎所有的數據庫訪問代碼。這一次咱們進一步實現 GraphQL 接口封裝炫乓。

一尤辱、GraphqQL 模式建立

  1. 基礎模式:
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",
})
  1. 基礎維度模式:
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",
})
  1. 賬號模式:
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",
})
  1. 維度模式(例如:閱讀金句):
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",
})
  1. 生態(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)建立

  1. 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
    },
}
  • 每個接口訪問時光督,除了接口必要的參數數據之外,還附帶上額外的用戶數據(idnickname)塔粒,方便以后的審計
  • content 是接口必要的參數數據结借,使用的是前面的文章中設計好的數據結構
  • 博客系統(tǒng)的所有接口參數盡量保持一致——這是一個能簡化邏輯的約定。
  1. 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 路由相關代碼實現卒茬。
?著作權歸作者所有,轉載或內容合作請聯系作者
  • 序言:七十年代末船老,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子圃酵,更是在濱河造成了極大的恐慌柳畔,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,816評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件郭赐,死亡現場離奇詭異薪韩,居然都是意外死亡,警方通過查閱死者的電腦和手機捌锭,發(fā)現死者居然都...
    沈念sama閱讀 90,729評論 3 385
  • 文/潘曉璐 我一進店門俘陷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人观谦,你說我怎么就攤上這事拉盾。” “怎么了豁状?”我有些...
    開封第一講書人閱讀 158,300評論 0 348
  • 文/不壞的土叔 我叫張陵捉偏,是天一觀的道長倒得。 經常有香客問我,道長夭禽,這世上最難降的妖魔是什么屎暇? 我笑而不...
    開封第一講書人閱讀 56,780評論 1 285
  • 正文 為了忘掉前任,我火速辦了婚禮驻粟,結果婚禮上根悼,老公的妹妹穿的比我還像新娘。我一直安慰自己蜀撑,他們只是感情好挤巡,可當我...
    茶點故事閱讀 65,890評論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著酷麦,像睡著了一般矿卑。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上沃饶,一...
    開封第一講書人閱讀 50,084評論 1 291
  • 那天母廷,我揣著相機與錄音,去河邊找鬼糊肤。 笑死琴昆,一個胖子當著我的面吹牛,可吹牛的內容都是我干的馆揉。 我是一名探鬼主播业舍,決...
    沈念sama閱讀 39,151評論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼升酣!你這毒婦竟也來了舷暮?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 37,912評論 0 268
  • 序言:老撾萬榮一對情侶失蹤噩茄,失蹤者是張志新(化名)和其女友劉穎下面,沒想到半個月后,有當地人在樹林里發(fā)現了一具尸體绩聘,經...
    沈念sama閱讀 44,355評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡沥割,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,666評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現自己被綠了君纫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片驯遇。...
    茶點故事閱讀 38,809評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖蓄髓,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情舒帮,我是刑警寧澤会喝,帶...
    沈念sama閱讀 34,504評論 4 334
  • 正文 年R本政府宣布陡叠,位于F島的核電站,受9級特大地震影響肢执,放射性物質發(fā)生泄漏枉阵。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 40,150評論 3 317
  • 文/蒙蒙 一预茄、第九天 我趴在偏房一處隱蔽的房頂上張望兴溜。 院中可真熱鬧,春花似錦耻陕、人聲如沸拙徽。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,882評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽骑科。三九已至弊琴,卻和暖如春个唧,著一層夾襖步出監(jiān)牢的瞬間俩檬,已是汗流浹背题画。 一陣腳步聲響...
    開封第一講書人閱讀 32,121評論 1 267
  • 我被黑心中介騙來泰國打工祭隔, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留篮灼,地道東北人忘古。 一個月前我還...
    沈念sama閱讀 46,628評論 2 362
  • 正文 我出身青樓,卻偏偏與公主長得像诅诱,于是被迫代替她去往敵國和親存皂。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,724評論 2 351

推薦閱讀更多精彩內容