• hasura的golang反向代理


    概述

    一直在寻找一个好用的 graphql 服务, 之前使用比较多的是 prisma, 但是 prisma1 很久不再维护了, 而 prisma2 仅仅就是一个 ORM, 不是一个完备的系统.

    后来, 朋友介绍了一个 graphql 引擎 hasura, 这个是完备的系统,
    不仅提供 UI 来创建数据库和表结构. 还有相应的权限控制, 也有对接第三方服务的 Events, 对数据更多控制的 Actions.

    为了使用其 graphql 接口, 同样, 和之前使用 prisma 时一样, 用 golang 的 gin 框架作为 gateway, 对 graphql 接口做一层反向代理.
    数据的操作基本都使用 hasura 的 graphql 接口, 逻辑比较复杂的, 或者是文件 上传/下载 相关的, 利用 gin 开发 restful 接口.

    反向代理代码

    对请求的处理

    1. 路由部分

      1  r := gin.Default()
      2  apiV1 := r.Group("/api/v1")
      3  
      4  // proxy hasura graphql
      5  authRoute.POST("/graphql", ReverseProxy())
      
    2. 处理请求的 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
      ", req.Header)
      18      }
      19  
      20      body, err := c.GetRawData()
      21      if err != nil {
      22        fmt.Printf("get body raw data: %s
      ", err)
      23      }
      24  
      25  fmt.Printf("%s
      ", 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")
    
  • 相关阅读:
    进程池和线程池、协程、TCP单线程实现并发
    GIL全局解释锁,死锁,信号量,event事件,线程queue,TCP服务端实现并发
    进程补充和线程的介绍
    进程的介绍和使用
    异常处理和UDP Socket套接字
    TCP Socket 套接字 和 粘包问题
    网络编程
    面向对象高级——反射和元类
    面向对象三大特性之——多态和一些内置函数
    面向对象-内置方法
  • 原文地址:https://www.cnblogs.com/wang_yb/p/13411282.html
Copyright © 2020-2023  润新知