string ResponseWriter.Write 和 io.WriteString 有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37863374/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What's the difference between ResponseWriter.Write and io.WriteString?
提问by laike9m
I've seen three ways of writing content to HTTP response:
我见过三种将内容写入 HTTP 响应的方法:
func Handler(w http.ResponseWriter, req *http.Request) {
io.WriteString(w, "blabla.\n")
}
And:
和:
func Handler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("blabla\n"))
}
Also there's:
还有:
fmt.Fprintf(w, "blabla")
What's the difference between them? Which one is preferred to use?
它们之间有什么区别?首选使用哪一种?
回答by icza
io.Writer
io.Writer
An output stream represents a target to which you can write sequence(s) of bytes. In Go this is captured by the general io.Writer
interface:
输出流代表一个目标,您可以向其写入字节序列。在 Go 中,这是由通用io.Writer
接口捕获的:
type Writer interface {
Write(p []byte) (n int, err error)
}
Everything that has this single Write()
method can be used as an output, for example a file on your disk (os.File
), a network connection (net.Conn
) or an in-memory buffer (bytes.Buffer
).
具有这种单一Write()
方法的所有内容都可以用作输出,例如磁盘上的文件 ( os.File
)、网络连接 ( net.Conn
) 或内存缓冲区 ( bytes.Buffer
)。
The http.ResponseWriter
that is used to configure the HTTP response and send the data to the client is also such an io.Writer
, the data you want to send (the response body) is assembled by calling (not necessarily just once) ResponseWriter.Write()
(which is to implement the general io.Writer
). This is the only guarantee you have about the implementation of the http.ResponseWriter
interface (regarding sending the body).
该http.ResponseWriter
用来配置HTTP响应并将数据发送给客户端也是这样的io.Writer
,你要发送(响应主体)的数据将通过调用(不一定只是一次)组装ResponseWriter.Write()
(这是实现一般io.Writer
) . 这是您对http.ResponseWriter
接口实现的唯一保证(关于发送正文)。
WriteString()
WriteString()
Now on to WriteString()
. Often we want to write textual data to an io.Writer
. Yes, we can do that simply by converting the string
to a []byte
, e.g.
现在到WriteString()
. 通常我们想将文本数据写入io.Writer
. 是的,我们可以简单地通过将 the 转换string
为 a来做到这一点[]byte
,例如
w.Write([]byte("Hello"))
which works as expected. However this is a very frequent operation and so there is a "generally" accepted method for this captured by the io.StringWriter
interface (available since Go 1.12, prior to that it was unexported):
它按预期工作。然而,这是一个非常频繁的操作,因此io.StringWriter
接口捕获了一个“普遍”接受的方法(自Go 1.12 开始可用,在此之前它未被导出):
type StringWriter interface {
WriteString(s string) (n int, err error)
}
This method gives the possibility to write a string
value instead of a []byte
. So if something (that also implements io.Writer
) implements this method, you can simply pass string
values without []byte
conversion. This seems like a minor simplification in code, but it's more than that.Converting a string
to []byte
has to make a copy of the string
content (because string
values are immutable in Go, read more about it here: golang: []byte(string) vs []byte(*string)), so there is some overhead which becomes noticeable if the string
is "bigger" and/or you have to do this many times.
此方法提供了写入string
值而不是的可能性[]byte
。因此,如果某些东西(也实现了io.Writer
)实现了此方法,则您可以简单地传递string
值而无需[]byte
转换。这似乎是代码中的一个小简化,但不仅如此。将 a 转换string
为[]byte
必须制作string
内容的副本(因为string
值在 Go 中是不可变的,请在此处阅读更多相关信息:golang: []byte(string) vs []byte(*string)),因此存在一些开销如果string
“更大”和/或您必须多次这样做,则很明显。
Depending on the nature and implementation details of an io.Writer
, it may be possible to write the content of a string
without converting it to []byte
and thus avoiding the above mentioned overhead.
根据 an 的性质和实现细节io.Writer
,可以在string
不将其转换为的情况下编写 a 的内容,[]byte
从而避免上述开销。
As an example, if an io.Writer
is something that writes to an in-memory buffer (bytes.Buffer
is such an example), it may utilize the builtin copy()
function:
例如,如果 anio.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 内置函数将元素从源切片复制到目标切片。(作为一种特殊情况,它还会将字节从字符串复制到字节切片。)
The copy()
may be used to copy the content (bytes) of a string
into a []byte
without converting the string
to []byte
, e.g.:
的copy()
可用于一个内容(字节)复制string
到一个[]byte
不转换string
到[]byte
,例如:
buf := make([]byte, 100)
copy(buf, "Hello")
Now there is a "utility" function io.WriteString()
that writes a string
into an io.Writer
. But it does this by first checking if the (dynamic type of the) passed io.Writer
has a WriteString()
method, and if so, that will be used (whose implementation is likely more efficient). If the passed io.Writer
has no such method, then the general convert-to-byte-slice-and-write-thatmethod will be used as a "fallback".
现在有一个“实用程序”函数io.WriteString()
可以将 astring
写入io.Writer
. 但是它首先检查传递的(动态类型)io.Writer
是否有WriteString()
方法,如果有,将使用该方法(其实现可能更有效)。如果传递的io.Writer
没有这样的方法,那么一般的转换为字节切片并写入该方法将用作“回退”。
You might think that this WriteString()
will only prevail in case of in-memory buffers, but that is not true. Responses of web requests are also often buffered (using an in-memory buffer), so it may improve performance in case of http.ResponseWriter
too. And if you look at the implementation of http.ResponseWriter
: it's the unexported type http.response
(server.go
currently line #308) which does implement WriteString()
(currently line #1212) so it does imply improvement.
您可能认为这WriteString()
只会在内存缓冲区的情况下占上风,但事实并非如此。Web 请求的响应也经常被缓冲(使用内存缓冲区),因此在这种情况下它可能会提高性能http.ResponseWriter
。如果您查看 : 的实现,http.ResponseWriter
它是未导出的类型http.response
(server.go
当前第 308 行),它确实实现了WriteString()
(当前第 1212 行),因此它确实意味着改进。
All in all, whenever you write string
values, recommended to use io.WriteString()
as it may be more efficient (faster).
总而言之,无论何时写入string
值,建议使用io.WriteString()
它,因为它可能更高效(更快)。
fmt.Fprintf()
fmt.Fprintf()
You should look at this as a convenient and easy way to add more formatting to the data you want to write, in exchange for being somewhat less performant.
您应该将其视为一种方便且简单的方法,可以为您要写入的数据添加更多格式,以换取性能稍差。
So use fmt.Fprintf()
if you want formatted string
created in the easy way, e.g.:
因此,fmt.Fprintf()
如果您想string
以简单的方式创建格式化,请使用,例如:
name := "Bob"
age := 23
fmt.Fprintf(w, "Hi, my name is %s and I'm %d years old.", name, age)
Which will result in the following string
to be written:
这将导致string
写入以下内容:
Hi, my name is Bob and I'm 23 years old.
One thing you must not forget: fmt.Fprintf()
expects a format string, so it will be preprocessed and not written as-is to the output. As a quick example:
您一定不要忘记一件事:fmt.Fprintf()
需要一个格式 string,因此它将被预处理并且不会按原样写入输出。举个简单的例子:
fmt.Fprintf(w, "100 %%")
You'd expect that "100 %%"
would be written to the output (with 2 %
characters), but only one will be sent as in the format string %
is a special character and %%
will only result in one %
in the output.
您希望"100 %%"
将其写入输出(带有 2 个%
字符),但只有一个会被发送,因为格式字符串%
是一个特殊字符,并且%%
只会%
在输出中产生一个。
If you just want to write a string
using the fmt
package, use fmt.Fprint()
which does not require a format string
:
如果你只想写一个string
usingfmt
包,使用fmt.Fprint()
它不需要格式string
:
fmt.Fprint(w, "Hello")
Another benefit of using the fmt
package is that you can write values of other types too, not just string
s, e.g.
使用该fmt
包的另一个好处是您也可以编写其他类型的值,而不仅仅是string
s,例如
fmt.Fprint(w, 23, time.Now())
(Of course the rules how to convert any value to a string
–and to series of bytes eventually–is well defined, in the doc of the fmt
package.)
(当然,如何将任何值转换为 astring
以及最终转换为一系列字节的规则在fmt
包的文档中有明确定义。)
For "simple" formatted outputs the fmt
package might be OK. For complex output documents do consider using the text/template
(for general text) and html/template
(whenever the output is HTML).
对于“简单”格式的输出,fmt
包可能没问题。对于复杂的输出文档,请考虑使用text/template
(对于一般文本)和html/template
(只要输出是 HTML)。
Passing / handing over http.ResponseWriter
传递/移交 http.ResponseWriter
For completeness, we should mention that often the content you want to send as the web response is generated by "something" that supports "streaming" the result. An example may be a JSON response, which is generated from a struct or map.
为了完整起见,我们应该提到,您想要作为 Web 响应发送的内容通常是由支持“流式传输”结果的“某物”生成的。一个例子可能是一个 JSON 响应,它是从结构或映射生成的。
In such cases it's often more efficient to pass / hand over your http.ResponseWriter
which is an io.Writer
to this somethingif it supports writing the result to an io.Writer
on-the-fly.
在这种情况下,如果它支持将结果写入即时http.ResponseWriter
,则传递/移交您的which is an io.Writer
to this something通常更有效io.Writer
。
A good example of this is generating JSON responses. Sure, you could marshal an object into JSON with json.Marshal()
, which returns you a byte slice, which you can simply send by calling ResponseWriter.Write()
.
一个很好的例子是生成 JSON 响应。当然,您可以使用 将对象编组到 JSON 中json.Marshal()
,它会返回一个字节切片,您可以通过调用ResponseWriter.Write()
.
However, it is more efficient to let the json
package know that you have an io.Writer
, and ultimately you want to send the result to that. That way it is unnecessary to first generate the JSON text in a buffer, which you just write into your response and then discard. You can create a new json.Encoder
by calling json.NewEncoder()
to which you can pass your http.ResponseWriter
as an io.Writer
, and calling Encoder.Encode()
after that will directly write the JSON result into your response writer.
但是,让json
包知道您有一个io.Writer
,并且最终您希望将结果发送给它会更有效。这样就没有必要首先在缓冲区中生成 JSON 文本,您只需将其写入响应然后丢弃。您可以json.Encoder
通过调用json.NewEncoder()
来创建一个新的,您可以将您的http.ResponseWriter
作为传递给它io.Writer
,然后调用Encoder.Encode()
将直接将 JSON 结果写入您的响应编写器。
One disadvantage here is that if generating the JSON response fails, you might have a partially sent / committed response which you cannot take back. If this is a problem for you, you don't really have a choice other than generating the response in a buffer, and if marshaling succeeds, then you may write the complete response at once.
这里的一个缺点是,如果生成 JSON 响应失败,您可能会收到部分发送/提交的响应,而您无法收回。如果这对您来说是个问题,除了在缓冲区中生成响应之外,您实际上别无选择,如果封送成功,那么您可以立即编写完整的响应。
回答by hitesh_noty
As you can see from here(ResponseWriter), that it is a interface with Write([]byte) (int, error)
method.
从这里可以看出(ResponseWriter),它是一个带有Write([]byte) (int, error)
方法的接口。
So in io.WriteString
and fmt.Fprintf
both are taking Writeras 1st argument which is also a interface wrapping Write(p []byte) (n int, err error)
method
所以 inio.WriteString
和fmt.Fprintf
两者都将Writer作为第一个参数,这也是一个接口包装Write(p []byte) (n int, err error)
方法
type Writer interface {
Write(p []byte) (n int, err error)
}
So when you call io.WriteString(w,"blah") check here
所以当你打电话 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))
}
or fmt.Fprintf(w, "blabla") check here
或 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
}
you are just calling Write Method indirectly as you are passing ResponseWriter
variable in both methods.
当您ResponseWriter
在两种方法中传递变量时,您只是间接调用 Write Method 。
So just why not call it directly using w.Write([]byte("blabla\n"))
. I hope you got your answer.
那么为什么不直接使用w.Write([]byte("blabla\n"))
. 我希望你得到你的答案。
PS: there's also a different way to use that, if you want to send it as JSON response.
PS:如果您想将其作为 JSON 响应发送,还有一种不同的使用方式。
json.NewEncoder(w).Encode(wrapper)
//Encode take interface as an argument. Wrapper can be:
//wrapper := SuccessResponseWrapper{Success:true, Data:data}