Go语言中http.Response接收函数的语义方式

huangapple go评论82阅读模式
英文:

Semantic way of http.Response receiver functions in Go

问题

我刚开始学习GO,并编写了这段代码,用于将http.Response.Body写入os.Stdout或文件中,但我对其语义不太满意。

我希望http.Response结构体具有这些接收函数,以便在整个应用程序中更轻松地使用它。

我知道答案可能会被标记为主观意见,但我仍然想知道,是否有更好的编写方式?是否有某种最佳实践?

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
)

type httpResp http.Response

func main() {
	res, err := http.Get("http://www.stackoverflow.com")
	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}
	defer res.Body.Close()

	response := httpResp(*res)

	response.toFile("stckovrflw.html")
	response.toStdOut()

}

func (r httpResp) toFile(filename string) {
	str, err := ioutil.ReadAll(r.Body)
	if err != nil {
		panic(err)
	}
	ioutil.WriteFile(filename, []byte(str), 0666)
}

func (r httpResp) toStdOut() {
	_, err := io.Copy(os.Stdout, r.Body)
	if err != nil {
		panic(err)
	}
}

另外,有没有办法使http.Get方法输出一个自定义类型,该类型已经具有这些接收函数的访问权限,而无需进行类型转换?这样我就可以像这样做:

func main() {
	res, err := http.Get("http://www.stackoverflow.com")
	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}
	defer res.Body.Close()

	res.toFile("stckovrflw.html")
	res.toStdOut()

}

谢谢!

英文:

I just started learning GO and wrote this piece of code that writes an http.Response.Body to os.Stdout or to a file, but I'm not happy about the semantics of this.

I want the http.Response struct to have these receiver functions, so I can use it more easily throughout the entire app.

I know that the answers might get flagged as opinionated, but I still wonder, is there a better way of writing this?
Is there some sort of best practice?

package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
)

type httpResp http.Response

func main() {
	res, err := http.Get("http://www.stackoverflow.com")
	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}
	defer res.Body.Close()

	response := httpResp(*res)

	response.toFile("stckovrflw.html")
	response.toStdOut()

}

func (r httpResp) toFile(filename string) {
	str, err := ioutil.ReadAll(r.Body)
	if err != nil {
		panic(err)
	}
	ioutil.WriteFile(filename, []byte(str), 0666)
}

func (r httpResp) toStdOut() {
    _, err := io.Copy(os.Stdout, r.Body)
    if err != nil {
	    panic(err)
    }
}

On a side note, is there a way to make the http.Get method spit out a custom type that already has access to these receiver functions without the need for casting? So i could do something like this:

func main() {
	res, err := http.Get("http://www.stackoverflow.com")
	if err != nil {
		fmt.Println("Error: ", err)
		os.Exit(1)
	}
	defer res.Body.Close()

	res.toFile("stckovrflw.html")
	res.toStdOut()

}

Thanks!

答案1

得分: 2

你不需要实现这些函数。*http.Response 已经实现了 io.Writer 接口(参考链接):

package main

import (
	"net/http"
	"os"
)

func main() {
	r := &http.Response{}
	r.Write(os.Stdout)
}

在上面的示例中,零值打印出:

HTTP/0.0 000 status code 0

Content-Length: 0

Playground: https://play.golang.org/p/2AUEAUPCA8j


如果你需要在写入方法中添加额外的业务逻辑,你可以在自定义类型中嵌入 *http.Response

type RespWrapper struct {
    *http.Response
}

func (w *RespWrapper) toStdOut() {
    _, err := io.Copy(os.Stdout, w.Body)
    if err != nil {
        panic(err)
    }
} 

但是你必须使用 *http.Response 构造一个 RespWrapper 类型的变量:

func main() {
    // 带有虚假 body 的 resp
	r := &http.Response{Body: io.NopCloser(strings.NewReader("foo"))}
    // 或者 r, _ := http.Get("example.com")

    // 构造 wrapper
	wrapper := &RespWrapper{Response: r}
	wrapper.toStdOut()
}

有没有办法使 http.Get 方法输出自定义类型?

不,http.Get 的返回类型是 (resp *http.Response, err error),这是函数签名的一部分,你不能改变它。

英文:

You don't have to implement these functions. *http.Response already implements io.Writer:

> Write writes r to w in the HTTP/1.x server response format, including the status line, headers, body, and optional trailer.

package main

import (
	"net/http"
	"os"
)

func main() {
	r := &http.Response{}
	r.Write(os.Stdout)
}

In the example above, the zero value prints:

> HTTP/0.0 000 status code 0
>
> Content-Length: 0

Playground: https://play.golang.org/p/2AUEAUPCA8j

<hr>

In case you need additional business logic in the write methods, you can embed *http.Response in your defined type:

type RespWrapper struct {
    *http.Response
}

func (w *RespWrapper) toStdOut() {
    _, err := io.Copy(os.Stdout, w.Body)
    if err != nil {
        panic(err)
    }
} 

But then you must construct a variable of type RespWrapper with the *http.Response:

func main() {
    // resp with a fake body
	r := &amp;http.Response{Body: io.NopCloser(strings.NewReader(&quot;foo&quot;))}
    // or r, _ := http.Get(&quot;example.com&quot;)

    // construct the wrapper
	wrapper := &amp;RespWrapper{Response: r}
	wrapper.toStdOut()
}

> is there a way to make the http.Get method spit out a custom type

No, the return types of http.Get are (resp *http.Response, err error), that's part of the function signature, you can't change it.

huangapple
  • 本文由 发表于 2021年9月19日 20:28:28
  • 转载请务必保留本文链接:https://go.coder-hub.com/69243281.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定