hasura的golang反向代理
概述
一直在寻找一个好用的 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")
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 按钮权限的设计及实现