ResponseWriter.Write 和 io.WriteString 有什么区别?
What's the difference between ResponseWriter.Write and io.WriteString?
我见过三种将内容写入 HTTP 响应的方法:
func Handler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "blabla.\n")
}
并且:
func Handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("blabla\n"))
}
还有:
fmt.Fprintf(w, "blabla")
它们有什么区别?更喜欢用哪一个?
从here(ResponseWriter)可以看出,它是一个带有Write([]byte) (int, error)
方法的接口。
所以在 io.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") check here
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") check here
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
}
您只是在间接调用 Write 方法,因为您在两种方法中都传递了 ResponseWriter
变量。
那么为什么不直接使用 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}
io.Writer
输出流代表一个目标,您可以向其写入字节序列。在 Go 中,这是由通用 io.Writer
接口捕获的:
type Writer interface {
Write(p []byte) (n int, err error)
}
所有具有这种单一 Write()
方法的东西都可以用作输出,例如磁盘上的文件 (os.File
), a network connection (net.Conn
) or an in-memory buffer (bytes.Buffer
)。
用于配置HTTP响应和向客户端发送数据的http.ResponseWriter
也是这样一个io.Writer
,你要发送的数据(响应体)由调用(不一定只是一次)ResponseWriter.Write()
(即实现通用io.Writer
)。这是您对 http.ResponseWriter
接口(关于发送正文)实现的唯一保证。
WriteString()
现在 WriteString()
。通常我们想将文本数据写入 io.Writer
。是的,我们可以简单地将 string
转换为 []byte
,例如
w.Write([]byte("Hello"))
按预期工作。然而,这是一个非常频繁的操作,因此有一个 "generally" 接受的方法,由 io.StringWriter
interface (available since Go 1.12 捕获,在此之前它未导出):
type StringWriter interface {
WriteString(s string) (n int, err error)
}
此方法可以写入 string
值而不是 []byte
。因此,如果某些东西(也实现了 io.Writer
)实现了这个方法,您可以简单地传递 string
值而无需 []byte
转换。 这似乎是对代码的轻微简化,但不仅如此。 将 string
转换为 []byte
必须复制 string
内容(因为 string
值在 Go 中是不可变的,在这里阅读更多相关信息:),所以如果 string
是 "bigger" and/or 你必须多次这样做。
根据 io.Writer
的性质和实现细节,可以写入 string
的内容而不将其转换为 []byte
,从而避免上述情况开销。
例如,如果 io.Writer
是写入内存缓冲区的东西(bytes.Buffer
就是这样的例子),它可能会利用内置的 copy()
函数:
The copy built-in function copies elements from a source slice into a destination slice. (As a special case, it also will copy bytes from a string to a slice of bytes.)
copy()
可用于将 string
的内容(字节)复制到 []byte
而无需将 string
转换为 []byte
,例如:
buf := make([]byte, 100)
copy(buf, "Hello")
现在有一个 "utility" 函数 io.WriteString()
可以将 string
写入 io.Writer
。但它通过首先检查传递的(动态类型的)io.Writer
是否有一个 WriteString()
方法来做到这一点,如果有,将使用该方法(其实现可能更有效)。如果传递的io.Writer
没有这样的方法,那么一般的convert-to-byte-slice-and-write-that方法将被用作"fallback" .
您可能认为 WriteString()
仅适用于内存缓冲区,但事实并非如此。 Web 请求的响应也经常被缓冲(使用内存缓冲区),因此它也可以在 http.ResponseWriter
的情况下提高性能。如果您查看 http.ResponseWriter
的实现:它是未导出的类型 http.response
(server.go
当前第 308 行)确实实现了 WriteString()
(当前第 1212 行)所以它确实意味着改善。
总而言之,无论何时写string
值,建议使用io.WriteString()
,因为它可能更有效(更快)。
fmt.Fprintf()
您应该将此视为向要写入的数据添加更多格式的便捷方式,但性能稍差。
因此,如果您想以简单的方式创建格式化的 string
,请使用 fmt.Fprintf()
,例如:
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 个 %
字符),但只有一个会被发送,因为格式字符串 %
是一个特殊字符和 %%
只会在输出中产生一个 %
。
如果您只想使用 fmt
包编写 string
,请使用不需要格式 string
:
的 fmt.Fprint()
fmt.Fprint(w, "Hello")
使用 fmt
包的另一个好处是你也可以写其他类型的值,而不仅仅是 string
s,例如
fmt.Fprint(w, 23, time.Now())
(当然,如何将任何值转换为 string
并最终转换为一系列字节的规则在 fmt
包的文档中定义明确。)
对于 "simple" 格式的输出,fmt
包可能没问题。对于复杂的输出文档,请考虑使用 text/template
(for general text) and html/template
(只要输出为 HTML)。
传递/移交http.ResponseWriter
为了完整起见,我们应该提到,您想要作为网络响应发送的内容通常是由支持 "streaming" 结果的 "something" 生成的。例如 JSON 响应,它是从结构或映射生成的。
在这种情况下,传递/移交你的 http.ResponseWriter
通常更有效,它是一个 io.Writer
给这个 something 如果它支持写结果即时 io.Writer
。
一个很好的例子就是生成 JSON 响应。当然,您可以使用 json.Marshal()
将对象编组到 JSON,其中 returns 是一个字节切片,您可以通过调用 ResponseWriter.Write()
.
简单地发送它
不过,让json
包知道你有一个io.Writer
会更高效,最终你要将结果发送给它。这样就没有必要首先在缓冲区中生成 JSON 文本,您只需将其写入响应然后丢弃。您可以创建一个新的 json.Encoder
by calling json.NewEncoder()
to which you can pass your http.ResponseWriter
as an io.Writer
, and calling Encoder.Encode()
,然后将 JSON 结果直接写入您的响应编写器。
这里的一个缺点是如果生成 JSON 响应失败,您可能有一个部分发送/提交的响应,您无法收回。如果这对您来说是个问题,除了在缓冲区中生成响应之外,您别无选择,如果编组成功,那么您可以立即写入完整的响应。
我见过三种将内容写入 HTTP 响应的方法:
func Handler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "blabla.\n")
}
并且:
func Handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("blabla\n"))
}
还有:
fmt.Fprintf(w, "blabla")
它们有什么区别?更喜欢用哪一个?
从here(ResponseWriter)可以看出,它是一个带有Write([]byte) (int, error)
方法的接口。
所以在 io.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") check here
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") check here
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
}
您只是在间接调用 Write 方法,因为您在两种方法中都传递了 ResponseWriter
变量。
那么为什么不直接使用 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}
io.Writer
输出流代表一个目标,您可以向其写入字节序列。在 Go 中,这是由通用 io.Writer
接口捕获的:
type Writer interface {
Write(p []byte) (n int, err error)
}
所有具有这种单一 Write()
方法的东西都可以用作输出,例如磁盘上的文件 (os.File
), a network connection (net.Conn
) or an in-memory buffer (bytes.Buffer
)。
用于配置HTTP响应和向客户端发送数据的http.ResponseWriter
也是这样一个io.Writer
,你要发送的数据(响应体)由调用(不一定只是一次)ResponseWriter.Write()
(即实现通用io.Writer
)。这是您对 http.ResponseWriter
接口(关于发送正文)实现的唯一保证。
WriteString()
现在 WriteString()
。通常我们想将文本数据写入 io.Writer
。是的,我们可以简单地将 string
转换为 []byte
,例如
w.Write([]byte("Hello"))
按预期工作。然而,这是一个非常频繁的操作,因此有一个 "generally" 接受的方法,由 io.StringWriter
interface (available since Go 1.12 捕获,在此之前它未导出):
type StringWriter interface {
WriteString(s string) (n int, err error)
}
此方法可以写入 string
值而不是 []byte
。因此,如果某些东西(也实现了 io.Writer
)实现了这个方法,您可以简单地传递 string
值而无需 []byte
转换。 这似乎是对代码的轻微简化,但不仅如此。 将 string
转换为 []byte
必须复制 string
内容(因为 string
值在 Go 中是不可变的,在这里阅读更多相关信息:string
是 "bigger" and/or 你必须多次这样做。
根据 io.Writer
的性质和实现细节,可以写入 string
的内容而不将其转换为 []byte
,从而避免上述情况开销。
例如,如果 io.Writer
是写入内存缓冲区的东西(bytes.Buffer
就是这样的例子),它可能会利用内置的 copy()
函数:
The copy built-in function copies elements from a source slice into a destination slice. (As a special case, it also will copy bytes from a string to a slice of bytes.)
copy()
可用于将 string
的内容(字节)复制到 []byte
而无需将 string
转换为 []byte
,例如:
buf := make([]byte, 100)
copy(buf, "Hello")
现在有一个 "utility" 函数 io.WriteString()
可以将 string
写入 io.Writer
。但它通过首先检查传递的(动态类型的)io.Writer
是否有一个 WriteString()
方法来做到这一点,如果有,将使用该方法(其实现可能更有效)。如果传递的io.Writer
没有这样的方法,那么一般的convert-to-byte-slice-and-write-that方法将被用作"fallback" .
您可能认为 WriteString()
仅适用于内存缓冲区,但事实并非如此。 Web 请求的响应也经常被缓冲(使用内存缓冲区),因此它也可以在 http.ResponseWriter
的情况下提高性能。如果您查看 http.ResponseWriter
的实现:它是未导出的类型 http.response
(server.go
当前第 308 行)确实实现了 WriteString()
(当前第 1212 行)所以它确实意味着改善。
总而言之,无论何时写string
值,建议使用io.WriteString()
,因为它可能更有效(更快)。
fmt.Fprintf()
您应该将此视为向要写入的数据添加更多格式的便捷方式,但性能稍差。
因此,如果您想以简单的方式创建格式化的 string
,请使用 fmt.Fprintf()
,例如:
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 个 %
字符),但只有一个会被发送,因为格式字符串 %
是一个特殊字符和 %%
只会在输出中产生一个 %
。
如果您只想使用 fmt
包编写 string
,请使用不需要格式 string
:
fmt.Fprint()
fmt.Fprint(w, "Hello")
使用 fmt
包的另一个好处是你也可以写其他类型的值,而不仅仅是 string
s,例如
fmt.Fprint(w, 23, time.Now())
(当然,如何将任何值转换为 string
并最终转换为一系列字节的规则在 fmt
包的文档中定义明确。)
对于 "simple" 格式的输出,fmt
包可能没问题。对于复杂的输出文档,请考虑使用 text/template
(for general text) and html/template
(只要输出为 HTML)。
传递/移交http.ResponseWriter
为了完整起见,我们应该提到,您想要作为网络响应发送的内容通常是由支持 "streaming" 结果的 "something" 生成的。例如 JSON 响应,它是从结构或映射生成的。
在这种情况下,传递/移交你的 http.ResponseWriter
通常更有效,它是一个 io.Writer
给这个 something 如果它支持写结果即时 io.Writer
。
一个很好的例子就是生成 JSON 响应。当然,您可以使用 json.Marshal()
将对象编组到 JSON,其中 returns 是一个字节切片,您可以通过调用 ResponseWriter.Write()
.
不过,让json
包知道你有一个io.Writer
会更高效,最终你要将结果发送给它。这样就没有必要首先在缓冲区中生成 JSON 文本,您只需将其写入响应然后丢弃。您可以创建一个新的 json.Encoder
by calling json.NewEncoder()
to which you can pass your http.ResponseWriter
as an io.Writer
, and calling Encoder.Encode()
,然后将 JSON 结果直接写入您的响应编写器。
这里的一个缺点是如果生成 JSON 响应失败,您可能有一个部分发送/提交的响应,您无法收回。如果这对您来说是个问题,除了在缓冲区中生成响应之外,您别无选择,如果编组成功,那么您可以立即写入完整的响应。