kevinyan815/gocookbook

用Golang发起和接收请求

kevinyan815 opened this issue · 0 comments

读取请求

接收请求涉及的内容较多,在我公众号的文章中已经有详细概况总结过,具体查看文章:「深入学习解析HTTP请求

发起请求

func httpRequest(method string, url string, options ...*Option) (code int, content string, err error) {
	start := time.Now()

	reqOpts := defaultRequestOptions() // 默认的请求选项
	for _, opt := range options {      // 在reqOpts上应用通过options设置的选项
		opt.apply(reqOpts)
	}
	// 创建请求对象
	req, err := http.NewRequest(method, url, strings.NewReader(reqOpts.data))
	// 记录请求日志
	defer func() {
		dur := int64(time.Since(start) / time.Second)
		if dur >= 500 {
			log.Println("Http"+method, url, "in", reqOpts.data, "out", content, "err", err, "dur/ms", dur)
		} else {
			log.Println("Http"+method, url, "in", reqOpts.data, "out", content, "err", err, "dur/ms", dur)
		}
	}()
	defer req.Body.Close()

	if len(reqOpts.headers) != 0 { // 设置请求头
		for key, value := range reqOpts.headers {
			req.Header.Add(key, value)
		}
	}
	if err != nil {
		return
	}
	// 发起请求
	client := &http.Client{Timeout: reqOpts.timeout}
	resp, error := client.Do(req)
	if error != nil {
		return 0, "", error
	}
	// 解析响应
	defer resp.Body.Close()
	code = resp.StatusCode
	result, _ := ioutil.ReadAll(resp.Body)
	content = string(result)

	return
}

// 发起GET请求
func HttpGet(url string, options ...*Option) (code int, content string, err error) {
	return httpRequest("GET", url, options...)
}

// 发起POST请求,请求头指定Content-Type: application/json
func HttpJsonPost(url string, data string, timeout time.Duration) (code int, content string, err error) {
	headers := map[string]string{
		"Content-Type": "application/json",
	}
	code, content, err = httpRequest(
		"POST", url, WithTimeout(timeout), WithHeaders(headers), WithData(data))

	return
}

由于HTTP请求时可以设置的选项较多,不固定,所以上面的代码学习gRPC对dialOptions的实现,使用了Options设计模式优雅地解决了不定参数的传递。

// 针对可选的HTTP请求配置项,模仿gRPC使用的Options设计模式实现
type requestOption struct {
	timeout time.Duration
	data    string
	headers map[string]string
}

type Option struct {
	apply func(option *requestOption)
}

func defaultRequestOptions() *requestOption {
	return &requestOption{ // 默认请求选项
		timeout: 5 * time.Second,
		data:    "",
		headers: nil,
	}
}

func WithTimeout(timeout time.Duration) *Option {
	return &Option{
		apply: func(option *requestOption) {
			option.timeout = timeout
		},
	}
}

详细代码,请查看仓库里的代码:https://github.com/kevinyan815/gocookbook/blob/master/codes/httptool/http_util.go