一直在尋找一個好用的 graphql 服務, 以前使用比較多的是 prisma, 可是 prisma1 好久再也不維護了, 而 prisma2 僅僅就是一個 ORM, 不是一個完備的系統.golang
後來, 朋友介紹了一個 graphql 引擎 hasura, 這個是完備的系統,
不只提供 UI 來建立數據庫和表結構. 還有相應的權限控制, 也有對接第三方服務的 Events, 對數據更多控制的 Actions.數據庫
爲了使用其 graphql 接口, 一樣, 和以前使用 prisma 時同樣, 用 golang 的 gin 框架做爲 gateway, 對 graphql 接口作一層反向代理.
數據的操做基本都使用 hasura 的 graphql 接口, 邏輯比較複雜的, 或者是文件 上傳/下載 相關的, 利用 gin 開發 restful 接口.json
路由部分api
1 r := gin.Default() 2 apiV1 := r.Group("/api/v1") 3 4 // proxy hasura graphql 5 authRoute.POST("/graphql", ReverseProxy())
處理請求的 body, 並轉發到 hasura 上restful
1 func ReverseProxy() gin.HandlerFunc { 2 3 u, err := url.Parse("your hasura graphql endpoint") 4 if err != nil { 5 log.Fatal(err) 6 } 7 8 return func(c *gin.Context) { 9 director := func(req *http.Request) { 10 req.URL.Scheme = u.Scheme 11 req.URL.Host = u.Host 12 req.URL.Path = u.Path 13 delete(req.Header, "Authorization") 14 delete(req.Header, "Accept-Encoding") 15 16 req.Header.Set("Content-Type", "application/json; charset=utf-8") 17 fmt.Printf("req header: %v\n", req.Header) 18 } 19 20 body, err := c.GetRawData() 21 if err != nil { 22 fmt.Printf("get body raw data: %s\n", err) 23 } 24 25 fmt.Printf("%s\n", string(body)) 26 27 c.Request.Body = ioutil.NopCloser(bytes.NewBuffer(body)) 28 29 proxy := &httputil.ReverseProxy{Director: director} 30 proxy.ModifyResponse = util.RewriteBody 31 proxy.ServeHTTP(c.Writer, c.Request) 32 } 33 }
這裏獲取了請求的 body, 可是沒有作任何處理, 直接轉發給 hasura. 這裏能夠根據實際狀況加入本身的處理.app
請求的問題解決以後, 就是返回值的處理, 之因此要對返回值進行處理, 是由於 hasura 直接返回的值沒有咱們自定義的一些 code.
因此須要對返回值進行一些包裝, 也就是上面代碼中的 RewriteBody框架
1 func RewriteBody(resp *http.Response) error { 2 b, err := ioutil.ReadAll(resp.Body) 3 if err != nil { 4 return err 5 } 6 7 err = resp.Body.Close() 8 if err != nil { 9 return err 10 } 11 12 var gResp GraphqlResp 13 var rResp RestResp 14 15 err = json.Unmarshal(b, &gResp) 16 if err != nil { 17 return err 18 } 19 20 if gResp.Errors != nil { 21 rResp = RestResp{ 22 Code: FAIL, 23 Message: gResp.Errors[0].Message, 24 Data: gResp.Data, 25 } 26 } else { 27 rResp = RestResp{ 28 Code: SUCCESS, 29 Message: "", 30 Data: gResp.Data, 31 } 32 } 33 34 nb, err := json.Marshal(&rResp) 35 if err != nil { 36 return err 37 } 38 body := ioutil.NopCloser(bytes.NewReader(nb)) 39 resp.Body = body 40 resp.ContentLength = int64(len(nb)) 41 resp.Header.Set("Content-Length", strconv.Itoa(len(nb))) 42 return nil 43 }
這樣, graphql 接口的返回值也和其餘本身寫的 restful 接口的返回值格式一致了.url
上面反向代理的代碼編寫過程當中, 遇到一個問題, 弄瞭解決了大半天才解決. 在請求 graphql 接口時, 始終報這個錯誤:代理
invalid character '\x1f' looking for beginning of value
解決方法很簡單, 就是上面的這行代碼:rest
1 delete(req.Header, "Accept-Encoding")