io.Writer
输出流表示可以向其写入字节序列的目标。在Go中,这是通过常规
io.Writer界面捕获的:
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为
[]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转换。
这似乎只是代码上的次要简化,但不仅限于此。
将转换
string为
[]byte必须复制
string内容(因为
stringGo中的值是不可变的,因此请在此处了解更多信息:golang:[]
byte(string)vs [] byte(*
string)),因此会产生一些开销如果
string“较大”和/或您必须多次执行此操作,则该提示会很明显。
根据an的性质和实现细节
io.Writer,有可能在
string不将a 转换为内容的情况下写入其内容,
[]byte从而避免了上述开销。
例如,如果an
io.Writer是写到内存缓冲区中的东西(
bytes.Buffer就是这样的例子),则它可以利用内置
copy()函数:
内置复制功能将元素从源切片复制到目标切片。 (作为一种特殊情况,它还会将字节从字符串复制到字节切片。)
的
copy()可用于一个内容(字节)复制
string到一个
[]byte不转换
string到
[]byte,例如:
buf := make([]byte, 100)copy(buf, "Hello")
现在有一个“实用程序”功能
io.WriteString(),可将a
string写入
io.Writer。但这是通过首先检查传递的(动态类型)
io.Writer是否具有
WriteString()方法来实现的,如果有,将使用该方法(其实现可能更有效)。如果所传递的
io.Writer内容没有这种方法,则将常规的
转换为字节切片并写入的 方法用作“后备”。
您可能会认为这
WriteString()仅在内存缓冲区中才有效,但事实并非如此。Web请求的响应也经常被缓冲(使用内存中的缓冲区),因此在有情况下也可以提高性能
http.ResponseWriter。而且,如果您看一下的实现
http.ResponseWriter:它是未导出的类型
http.response(
server.go当前为#308行)可以实现
WriteString()(当前为#1212行),因此它确实意味着有所改进。
总之,每当您编写
string值时,建议使用
io.WriteString()它,因为它可能会更有效(更快)。
fmt.Fprintf()
您应该将其视为一种便捷的方法,为要写入的数据添加更多格式,以换取性能稍差的东西。
因此,
fmt.Fprintf()如果要
string以简单的方式创建格式,请使用,例如:
name := "Bob"age := 23fmt.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.Writer这个 东西
,如果它支持将结果写到一个
io.Writer关于即时。
一个很好的例子是生成JSON响应。当然,您可以使用来将对象编组为JSON
json.Marshal(),这将返回一个字节片,您只需调用即可发送该字节片
ResponseWriter.Write()。
但是,让
json程序包知道您有个
io.Writer,这样效率更高,最终您想将结果发送给该包。这样,就不必先在缓冲区中生成JSON文本,您只需将其写入响应中然后丢弃即可。您可以
json.Enprer通过调用
json.NewEnprer()来创建新
http.ResponseWriter的
io.Writer,并将用作,然后调用
Enprer.Enpre(),然后直接将JSON结果写入响应编写器。
这里的一个缺点是,如果生成JSON响应失败,则可能会有部分发送/提交的响应,您将无法收回该响应。如果这对您来说是个问题,那么除了在缓冲区中生成响应之外,您别无选择,并且如果封送处理成功,则可以立即编写完整的响应。



