hasura的golang反向代理
阿新 • • 發佈:2020-07-31
概述
一直在尋找一個好用的 graphql 服務, 之前使用比較多的是 prisma, 但是 prisma1 很久不再維護了, 而 prisma2 僅僅就是一個 ORM, 不是一個完備的系統.
後來, 朋友介紹了一個 graphql 引擎 hasura, 這個是完備的系統,
不僅提供 UI 來建立資料庫和表結構. 還有相應的許可權控制, 也有對接第三方服務的 Events, 對資料更多控制的 Actions.
為了使用其 graphql 介面, 同樣, 和之前使用 prisma 時一樣, 用 golang 的 gin 框架作為 gateway, 對 graphql 介面做一層反向代理.
資料的操作基本都使用 hasura 的 graphql 介面, 邏輯比較複雜的, 或者是檔案 上傳/下載 相關的, 利用 gin 開發 restful 介面.
反向代理程式碼
對請求的處理
-
路由部分
1 r := gin.Default() 2 apiV1 := r.Group("/api/v1") 3 4 // proxy hasura graphql 5 authRoute.POST("/graphql", ReverseProxy())
-
處理請求的 body, 並轉發到 hasura 上
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. 這裡可以根據實際情況加入自己的處理.
對返回值的處理
請求的問題解決之後, 就是返回值的處理, 之所以要對返回值進行處理, 是因為 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 介面的返回值格式一致了.
遇到的問題
上面反向代理的程式碼編寫過程中, 遇到一個問題, 弄瞭解決了大半天才解決. 在請求 graphql 介面時, 始終報這個錯誤:
invalid character '\x1f' looking for beginning of value
解決方法很簡單, 就是上面的這行程式碼:
1 delete(req.Header, "Accept-Encoding")