2 回答
TA贡献1840条经验 获得超5个赞
io.Writer
输出流表示您可以向其写入字节序列的目标。io.Writer在 Go 中,这是由通用接口捕获的:
type Writer interface {
Write(p []byte) (n int, err error)
}
具有这种单一Write()方法的所有内容都可以用作输出,例如磁盘上的文件 ( os.File)、网络连接 ( net.Conn) 或内存缓冲区 ( bytes.Buffer)。
http.ResponseWriter用来配置HTTP响应和发送数据给客户端的也是这样一个,io.Writer你要发送的数据(响应体)是通过调用组装的(不一定只有一次)ResponseWriter.Write()(这是实现通用的io.Writer) . 这是您对http.ResponseWriter接口实现的唯一保证(关于发送主体)。
WriteString()
现在继续WriteString()。通常我们想将文本数据写入io.Writer. 是的,我们可以简单地通过将 转换string为 a来做到这一点[]byte,例如
w.Write([]byte("Hello"))
按预期工作。然而,这是一个非常频繁的操作,因此io.StringWriter接口捕获了一个“普遍”接受的方法(从Go 1.12开始可用,在此之前它未被导出):
type StringWriter interface {
WriteString(s string) (n int, err error)
}
此方法提供了写入string值而不是的可能性[]byte。所以如果某些东西(也实现了io.Writer)实现了这个方法,你可以简单地传递string值而无需[]byte转换。这似乎是对代码的轻微简化,但不仅如此。将 a 转换string为[]byte必须复制string内容(因为string值在 Go 中是不可变的,请在此处阅读有关它的更多信息:golang: []byte(string) vs []byte(*string)),因此会产生一些开销如果string“更大”和/或您必须多次执行此操作,则很明显。
根据 a 的性质和实现细节,io.Writer可以编写 a 的内容string而不将其转换为[]byte,从而避免上述开销。
例如,如果 anio.Writer是写入内存缓冲区的东西(bytes.Buffer就是这样一个例子),它可以利用内置copy()函数:
复制内置函数将元素从源切片复制到目标切片。(作为一种特殊情况,它还将字节从字符串复制到字节片。)
可copy()用于将 a 的内容(字节)复制string到 a[]byte而无需将 转换string为[]byte,例如:
buf := make([]byte, 100)
copy(buf, "Hello")
现在有一个“实用程序”函数io.WriteString()可以将 astring写入io.Writer. 但它首先检查传递的(动态类型)io.Writer是否有WriteString()方法,如果有,将使用该方法(其实现可能更有效)。如果传递io.Writer的没有这样的方法,那么一般的convert-to-byte-slice-and-write-that方法将被用作“后备”。
您可能认为这WriteString()只会在内存缓冲区的情况下占优势,但事实并非如此。Web 请求的响应也经常被缓冲(使用内存缓冲区),因此它也可以提高性能http.ResponseWriter。如果您查看 : 的实现,http.ResponseWriter它是未导出的类型http.response(server.go当前第 308 行),它确实实现了WriteString()(当前第 1212 行),所以它确实意味着改进。
总而言之,无论何时写入string值,都建议使用它,io.WriteString()因为它可能更有效(更快)。
fmt.Fprintf()
您应该将此视为一种方便且简单的方法,可以为要写入的数据添加更多格式,以换取性能稍差。
因此fmt.Fprintf(),如果您想string以简单的方式创建格式化,请使用,例如:
name := "Bob"
age := 23
fmt.Fprintf(w, "Hi, my name is %s and I'm %d years old.", name, age)
这将导致以下内容string被写入:
Hi, my name is Bob and I'm 23 years old.
您不能忘记的一件事:fmt.Fprintf()需要一个格式字符串,因此它将被预处理而不是按原样写入输出。举个简单的例子:
fmt.Fprintf(w, "100 %%")
您希望"100 %%"将其写入输出(带有 2 个%字符),但只会发送一个,因为格式字符串%是一个特殊字符,并且%%只会%在输出中产生一个。
如果您只想string使用fmt包编写一个,请使用fmt.Fprint()不需要格式string:
fmt.Fprint(w, "Hello")
使用该包的另一个好处fmt是您也可以编写其他类型的值,而不仅仅是strings,例如
fmt.Fprint(w, 23, time.Now())
(当然,如何将任何值转换为一个string- 并最终转换为一系列字节的规则在fmt包的文档中得到了很好的定义。)
对于“简单”格式的输出,fmt包可能没问题。对于复杂的输出文档,请考虑使用text/template(对于一般文本)和html/template(只要输出是 HTML)。
传递/移交 http.ResponseWriter
为了完整起见,我们应该提到,您希望作为 Web 响应发送的内容通常是由支持“流式传输”结果的“某物”生成的。一个示例可能是从结构或映射生成的 JSON 响应。
在这种情况下,如果它支持将结果写入即时消息http.ResponseWriter,则传递/移交您的内容通常会更有效。io.Writerio.Writer
一个很好的例子是生成 JSON 响应。当然,您可以使用 将一个对象编组为 JSON json.Marshal(),它会返回一个字节切片,您可以通过调用简单地发送它ResponseWriter.Write()。
然而,让json包知道你有一个io.Writer,并且最终你想将结果发送给它会更有效。这样就不必首先在缓冲区中生成 JSON 文本,您只需将其写入响应然后丢弃。json.Encoder您可以通过调用来创建一个新的json.NewEncoder(),您可以将您的http.ResponseWriteras传递给它io.Writer,然后调用Encoder.Encode()将直接将 JSON 结果写入您的响应编写器。
这里的一个缺点是,如果生成 JSON 响应失败,您可能会收到部分发送/提交的响应,您无法收回。如果这对您来说是个问题,那么您除了在缓冲区中生成响应之外别无选择,如果封送处理成功,那么您可以立即编写完整的响应。
TA贡献1828条经验 获得超13个赞
从这里(ResponseWriter)可以看出,它是一个带有Write([]byte) (int, error)方法的接口。
所以 inio.WriteString和fmt.Fprintf两者都将Writer作为第一个参数,这也是一种接口包装Write(p []byte) (n int, err error)方法
type Writer interface {
Write(p []byte) (n int, err error)
}
因此,当您调用 io.WriteString(w,"blah") 时,请检查此处
func WriteString(w Writer, s string) (n int, err error) {
if sw, ok := w.(stringWriter); ok {
return sw.WriteString(s)
}
return w.Write([]byte(s))
}
或 fmt.Fprintf(w, "blabla")在这里检查
func Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
p := newPrinter()
p.doPrintf(format, a)
n, err = w.Write(p.buf)
p.free()
return
}
您只是ResponseWriter在两种方法中传递变量时间接调用 Write Method。
所以为什么不直接使用w.Write([]byte("blabla\n")). 我希望你得到你的答案。
PS:如果您想将其作为 JSON 响应发送,还有一种不同的使用方式。
json.NewEncoder(w).Encode(wrapper)
//Encode take interface as an argument. Wrapper can be:
//wrapper := SuccessResponseWrapper{Success:true, Data:data}
- 2 回答
- 0 关注
- 547 浏览
添加回答
举报