基于Go语言实现好用的HTTP接口请求requests

使用Go自带的net/http库可以发送各种HTTP请求。
然而各种类型请求发送方式有点不太一致,这里参考Python requests库的使用方式,简单封装了一下。
代码如下:

文件requests.go内容

package main

import (
    "encoding/json"
    "io"
    "io/ioutil"
    "net/http"
    "net/url"
    "strings"
    "time"
)

// 请求结构体
type Request struct {
    Method string json:"method"               // 请求方法
    Url string json:"url"                     // 请求url
    Params map[string]string json:"params"    // Query参数
    Headers map[string]string json:"headers"  // 请求头
    Cookies map[string]string json:"cookies"  // todo 处理 Cookies
    Data map[string]string json:"data"        // 表单格式请求数据
    Json map[string]string json:"json"        // JSON格式请求数据 todo 多层 嵌套
    Files map[string]string json:"files"      // todo 处理 Files
    Raw string json:"raw"                     // 原始请求数据
}

// 响应结构体
type Response struct {
    StatusCode int json:"status_code"         // 状态码
    Reason string json:"reason"               // 状态码说明
    Elapsed    float64  json:"elapsed"        // 请求耗时(秒)
    Content    [] byte  json:"content"        // 响应二进制内容
    Text       string  json:"text"            // 响应文本
    Headers    map[string]string json:"headers" // 响应头
    Cookies    map[string]string json:"cookies" // todo 添加响应Cookies
    Request    *Request json:"request"          // 原始请求
}

// 处理请求方法
func (r *Request) getMethod() string{
    return strings.ToUpper(r.Method)              // 必须转为全部大写
}

// 组装URL
func (r *Request) getUrl() string {
    if r.Params != nil{
        urlValues := url.Values{}
        Url, _ := url.Parse(r.Url)  // todo 处理err
        for key, value := range(r.Params){
            urlValues.Set(key,value)
        }
        Url.RawQuery = urlValues.Encode()
        return Url.String()
    }
    return r.Url
}

// 组装请求数据
func (r *Request) getData() io.Reader {
    var reqBody string
    if r.Raw != "" {
        reqBody = r.Raw
    }else if r.Data != nil{
        urlValues := url.Values{}
        for key, value := range(r.Data){
            urlValues.Add(key,value)
        }
        reqBody = urlValues.Encode()
        r.Headers["Content-Type"] = "application/x-www-form-urlencoded"
    }else if r.Json != nil{
        bytesData, _ := json.Marshal(r.Json)
        reqBody = string(bytesData)
        r.Headers["Content-Type"] = "application/json"
    }
    return strings.NewReader(reqBody)
}

// 添加请求头-需要在getData后使用
func (r *Request) addHeaders(req *http.Request){
    if r.Headers != nil{
        for key, value := range(r.Headers){
            req.Header.Add(key, value)
        }
    }
}

// 准备请求
func (r *Request) prepare() *http.Request{
    Method := r.getMethod()
    Url := r.getUrl()
    Data := r.getData()
    req, _ := http.NewRequest(Method, Url, Data)
    r.addHeaders(req)
    return req
}

// 组装响应对象
func (r *Request) packResponse(res *http.Response, elapsed float64) Response {
    var resp Response
    resBody, _ := ioutil.ReadAll(res.Body)
    resp.Content = resBody
    resp.Text = string(resBody)
    resp.StatusCode = res.StatusCode
    resp.Reason = strings.Split(res.Status, " ")[1]
    resp.Elapsed = elapsed
    resp.Headers = map[string]string{}
    for key, value := range(res.Header){
        resp.Headers[key] = strings.Join(value, ";")
    }
    return resp
}

// 发送请求
func (r *Request) Send() Response {
    req := r.prepare()
    client := &http.Client{}
    start := time.Now()
    res, _ := client.Do(req)  // todo 处理err
    defer res.Body.Close()
    elapsed := time.Since(start).Seconds()
    return r.packResponse(res, elapsed)
}

使用方法如下:
文件requests_test.go内容

package main

import (
    "fmt"
    "testing"
)

// 发送GET 请求
func TestGet(t *testing.T){
    r := Request{
        Method: "GET",
        Url: "https://httpbin.org/get?name=张三&age=12",
        Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
    resp := r.Send()
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("原因: %s\n", resp.Reason)
    fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
    fmt.Printf("响应文本: %s\n", resp.Text)
}

// 发送GET 带单独Query参数请求
func TestGetWithParams(t *testing.T){
    r := Request{
        Method: "GET",
        Url: "https://httpbin.org/get",
        Params: map[string]string{"name": "张三", "age": "12"},
        Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
    resp := r.Send()
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("原因: %s\n", resp.Reason)
    fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
    fmt.Printf("响应文本: %s\n", resp.Text)
}

// 发送POST 表单请求
func TestPostForm(t *testing.T){
    r := Request{
        Method: "POST",
        Url: "https://httpbin.org/post",
        Data: map[string]string{"name": "张三", "age": "12"},
        Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
    resp := r.Send()
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("原因: %s\n", resp.Reason)
    fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
    fmt.Printf("响应文本: %s\n", resp.Text)
}

// 发送POST JSON请求
func TestPostJson(t *testing.T){
    r := Request{
        Method: "POST",
        Url: "https://httpbin.org/post",
        Json: map[string]string{"name": "张三", "age": "12"},
        Headers: map[string]string{"Cookie": "abc", "Token": "123"}}
    resp := r.Send()
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("原因: %s\n", resp.Reason)
    fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
    fmt.Printf("响应文本: %s\n", resp.Text)
}

也可以读取json文件并发送请求,示例如下:
文件data.json内容

{
  "method": "post",
  "url": "https://httpbin.org/post",
  "data": {"name": "Kevin", "age":  12}
}

文件requests_test.go内容

// 读取JSON文件发送请求
func TestRequestFromJsonFile(t *testing.T) {
    jsonFile, err := os.Open("data.json")
    if err != nil {
        fmt.Println("打开文件失败")
    }
    defer jsonFile.Close()
    jsonData, err := ioutil.ReadAll(jsonFile)
    if err != nil {
        fmt.Println("读取JSON文件失败")
    }

    r := Request{
        Params: map[string]string{},
        Headers: map[string]string{},
        Data: map[string]string{},
        Json: map[string]string{},
        Files: map[string]string{}}
    err = json.Unmarshal(jsonData, &r)
    if err != nil {
        fmt.Printf("反序列化出错")
    }
    fmt.Println(r)
    resp := r.Send()
    fmt.Printf("状态码: %d\n", resp.StatusCode)
    fmt.Printf("原因: %s\n", resp.Reason)
    fmt.Printf("响应时间: %f秒\n", resp.Elapsed)
    fmt.Printf("响应文本: %s\n", resp.Text)
}

参考:golang常用的http请求操作
传送门:asmcos版

Original: https://www.cnblogs.com/superhin/p/16332720.html
Author: 韩志超
Title: 基于Go语言实现好用的HTTP接口请求requests

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/536355/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Go sort包

    sort包简介 官方文档Golang的sort包用来排序,二分查找等操作。本文主要介绍sort包里常用的函数,通过实例代码来快速学会使用sort包 sort包内置函数 sort.I…

    Go语言 2023年5月25日
    093
  • JetBrains GoLand 2022 (GO语言集成开发工具环境)

    Original: https://www.cnblogs.com/aurora-123/p/16534930.htmlAuthor: 佛系女孩Title: JetBrains G…

    Go语言 2023年5月25日
    050
  • Go语言基础笔记

    Go语言基础语法:https://note.youdao.com/noteshare?id=12098a23b490260282f6b7c347ab182f Go语言内建容器:ht…

    Go语言 2023年5月29日
    070
  • Sentinel-Go 源码系列(一)|开篇

    大家好呀,打算写一个 Go 语言组件源码分析系列,一是为了能学习下 Go 语言,看下别人是怎么写 Go 的,二是也掌握一个组件。 本次选择了 Sentinel-Go,一是对 Jav…

    Go语言 2023年5月25日
    058
  • go-micro开发RPC服务的方法及其运行原理

    go-micro是一个知名的golang微服务框架,最新版本是v4,这篇文章将介绍go-micro v4开发RPC服务的方法及其运作原理。 基本概念 go-micro有几个重要的概…

    Go语言 2023年5月25日
    049
  • Go xmas2020 学习笔记 01-14 上篇

    课程地址 go-class-slides/xmas-2020 at trunk · matt4biz/go-class-slides (github.com) 主讲老师 Matt …

    Go语言 2023年5月25日
    070
  • sync:一. 原子操作(atomic)

    原子操作是指在程序运行中不能被中断的操作,原子操作是无锁的常常是由CPU指令直接实现,而锁一般由操作系统的调度器实现,所以原子操作的效率一般更高。 golang中原子操作支持的类型…

    Go语言 2023年5月25日
    050
  • 在开发中将git运用自如

    写这篇文章的初衷是记录自己在开发中使用git遇到的问题和如何利用git进行高效的开发。个人理解来看,很多人对git运用不自如主要是两方面的原因:1、死记硬背命令,这个其实可以通过h…

    Go语言 2023年5月25日
    042
  • GO语言程序查询数据库字段为空遇到的几个问题总结

    如果字段值可能为空,那么从表里面读取数据的时候程序使用的变量类型应该使用 sql.NullXXX 类型,比如下面的日期类型: 开始的时候,deleteAt 使用的也是 time.T…

    Go语言 2023年5月25日
    068
  • Golang – 关于 proto 文件的一点小思考

    ProtoBuf 是什么? ProtoBuf 是一套接口描述语言(IDL),通俗的讲是一种数据表达方式,也可以称为数据交换格式。 我们常用的数据格式有 JSON 和 XML,为什么…

    Go语言 2023年5月25日
    054
  • B树-查找

    B树系列文章 1. B树-介绍 2. B树-查找 3. B树-插入 4. B树-删除 查找 假设有一棵3阶B树,如下图所示。 下面说明在该B树中查找 52的过程 首先,从根结点出发…

    Go语言 2023年5月25日
    058
  • 大道如青天,协程来通信,Go lang1.18入门精炼教程,由白丁入鸿儒,Go lang通道channel的使用EP14

    众所周知,Go lang的作用域相对严格,数据之间的通信往往要依靠参数的传递,但如果想在多个协程任务中间做数据通信,就需要通道(channel)的参与,我们可以把数据封装成一个对象…

    Go语言 2023年5月25日
    076
  • 服务注册与发现的原理和实现

    什么是服务注册发现? 对于搞微服务的同学来说,服务注册、服务发现的概念应该不会太陌生。 简单来说,当服务A需要依赖服务B时,我们就需要告诉服务A,哪里可以调用到服务B,这就是服务注…

    Go语言 2023年5月25日
    051
  • go微服务框架Kratos笔记(二)引入zap日志库

    zap日志库是一款高性能的开源日志库,提供了结构化日志记录和printf风格的日志记录 go get -u go.uber.org/zap 参考官方文档中描述,为了方便业务自适配不…

    Go语言 2023年5月25日
    045
  • Go语言之高级篇beego框架之view

    1、基本语法 go统一使用了{{ 和 }}作为左右标签,没有其它的标签符号。 如果你想要修改为其它符号,可以修改配置文件。 使用.来访问当前位置的上下文 使用$来引用当前模板根级的…

    Go语言 2023年5月29日
    059
  • mit6.824 lab1 (2022)

    go;gutter:true; lab1 要求按照论文实现一个mapReduce 框架</p> <pre><code> ;gutter:true…

    Go语言 2023年5月25日
    048
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球