• Go Http Get 和 Post 工具函数


    前言

    先说一下为什么要搞这个小东西?

    米攸服务端前期主要是基于 Go 构建的,版本迭代过程中,业务复杂度不断增加,再加上中员团队有人员变动,考虑到目前团队的技术背景,我们开始考虑把接口服务分批迁移到 Java,开发效率和可控程度更高一些。其中有一些接口服务涉及周边模块较多,迁移的时间成本较高,我们决定暂时继续维护这些接口。后续接口需要升级时,如果变动较小,我们直接修改 Go 代码;如果变动较大,我们在 Go 代码中使用 HTTP 的方式调用 Java 接口实现,相当于给原有接口加了一个 钩子。为了减化接口调用代码编写的复杂度,我们考虑在 Go 代码中内置两个工具函数:GetPost,方便调用 Java 接口。

    本文重点讨论 Get 和 Post 函数实现的关键细节,并给出核心代码。

    Result

    Java 接口的返回结果是一个 固定格式Json 字符串:

    • id

      请求ID,字符串。

    • code

      状态码,整数。

    • msg

      状态信息,字符串。

    • data

      数据,任意类型。

    我们使用 结构体 封装返回结果:

      type Result struct {
        Id   string      `json:"id"`
        Code int         `json:"code"`
        Msg  string      `json:"msg"`
        Data interface{} `json:"data"`
      }
    

    可以发现,结构体的字段名称和返回结果的字段名称是不一样的(首字母大小写),两者相互转换的时候名称会对应不上,需要在结构体中使用类似 `json:"id"` 的声明,把结构体中的字段名称和返回结果的字段名称一一对应起来。

    特别注意:声明字段名称时标点符号的使用。

    Client

    Go 提供的 Http 客户端(Client)实例是线程安全的,一个进程内只需要有一个即可:

      var client = http.Client{}
    

    Get

    Get 函数的参数应该有两个:接口路径(url)和 接口参数(params)。接口路径比较简单,就是一个字符串(string),我们主要讨论接口参数。

    我们使用 Query String 的方式传递 Get 参数,如:/interface/param1=value1&param2=value2,接口参数的类型应该是一个内部包含多个键值对的 字典(map[string]interface{}),键名称是参数名称,键值是参数值;考虑到实际使用场景,参数值的类型限制为三种:

    • string
    • int
    • float64

    因为参数的值类型是不确定的,所以使用 interface{} 表示任意类型,函数内部判断具体类型。

    因为调用接口时需要添加 请求头请求参数,所以不能直接使用 http.Get 这样的简化函数,实现流程:

    创建请求

    使用 http.NewRequest 创建请求:

      req, err := http.NewRequest("GET", url, nil) 
    

    添加请求头

    设置请求响应的内容类型为 json:

      req.Header.Add("Content-Type", "application/json")
    

    设置调用接口时的 Token:

      req.Header.Add("token", MEETU_API_TOKEN) 
    

    添加请求参数

    创建请求参数:

      query := req.URL.Query() 
    

    逐个添加请求参数:

     query.Add(name, value) 
    

    注意:添加请求参数时,name(参数名称) 和 value(参数类型) 类型都是字符串。

    如前文所述,接口参数是一个字典类型的变量,我们需要遍历这个变量中的每一个键值对,逐个添加参数。遍历可以使用 Range

      for name, value := range params {
        ...
      }
    

    如前文所述,参数值类型是有限制的,遍历过程中,我们需要判断参数值类型是否符合要求。类型判断可以使用 value.(type)

      switch value.(type) {
      case string:
        query.Add(name, value.(string))
    
      case int:
        query.Add(name, strconv.Itoa(value.(int)))
    
      case float64:
        query.Add(name, strconv.FormatFloat(value.(float64), 'f', -1, 64))
    
      default:
        return Result{}, errors.New("params type only support string, int and float64")
      }
    

    使用 value.(string)、value.(int) 和 value.(float64) 把变量 value(类型:interface{}) 分别转换为类型 string、int 和 float64 的变量。使用 strconv.Itoa 把 int 变量转换为 string 变量,使用 strconv.FormatFloat 把 float64 变量转换为 string 变量。

    请求参数值可能包含特殊字符,需要转义:

      req.URL.RawQuery = query.Encode()
    

    请求参数添加完成。

    执行请求,获取结果

      resp, err := client.Do(req)
    
      defer resp.Body.Close()
    

    defer 表示 Get 函数执行完成之后,关闭 Http 客户端内部的网络连接。

    解析结果

    响应体(resp.Body)的数据是字节流,需要解码并反序列化成类型为 Result 的变量 result:

      json.NewDecoder(resp.Body).Decode(&result)
    

    返回结果

      return result, nil 
    

    到此,Get 函数实现完成,代码如下:

      func Get(url string, params map[string]interface{}) (Result, error) {
        req, err := http.NewRequest("GET", url, nil)
        if err != nil {
          return Result{}, err
        }
    
        req.Header.Add("Content-Type", "application/json")
        req.Header.Add("token", MEETU_API_TOKEN)
    
        if params != nil {
          query := req.URL.Query()
    
          for name, value := range params {
            switch value.(type) {
            case string:
              query.Add(name, value.(string))
    
            case int:
              query.Add(name, strconv.Itoa(value.(int)))
    
            case float64:
              query.Add(name, strconv.FormatFloat(value.(float64), 'f', -1, 64))
    
            default:
              return Result{}, errors.New("params type only support string, int and float64")
            }
          }
    
          req.URL.RawQuery = query.Encode()
        }
    
        resp, err := client.Do(req)
        if err != nil {
          return Result{}, err
        }
    
        defer resp.Body.Close()
    
        var result Result
    
        err = json.NewDecoder(resp.Body).Decode(&result)
        if err != nil {
          return Result{}, err
        }
    
        return result, nil
      }
    

    Post

    Post 函数的实现过程整体和 Get 是类似的,唯一不同的就是请求参数的处理。我们使用 Body 传递 Post 参数。

    其余内容请参考:Go Http Get 和 Post 工具函数

  • 相关阅读:
    聊聊我们是如何做技术保障的
    JVM阶段(2)-对象
    数据屏蔽:静态与动态
    matlab GUI界面实现ZieglerNicholas调节PID参数
    单例模式(饿汉式单例 VS 懒汉式单例)
    springcloud feign配置及调用
    java八股文面试[数据库]——数据库锁的种类
    推荐一款.NET开源跨平台的开箱即用的DNS服务器软件
    数商云:订单积压达 930 亿欧元,西门子如何通过供应链数字化转型缩短交货期
    【Robotframework+python】实现http接口自动化测试
  • 原文地址:https://www.cnblogs.com/yurunmiao/p/16261124.html