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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 16:32:38  来源:igfitidea点击:

What's the difference between ResponseWriter.Write and io.WriteString?

stringhttpgoslice

提问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.Writerinterface:

输出流代表一个目标,您可以向其写入字节序列。在 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.ResponseWriterthat 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.ResponseWriterinterface (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 stringto 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.StringWriterinterface (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 stringvalue instead of a []byte. So if something (that also implements io.Writer) implements this method, you can simply pass stringvalues without []byteconversion. This seems like a minor simplification in code, but it's more than that.Converting a stringto []bytehas to make a copy of the stringcontent (because stringvalues 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 stringis "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 stringwithout converting it to []byteand thus avoiding the above mentioned overhead.

根据 an 的性质和实现细节io.Writer,可以在string不将其转换为的情况下编写 a 的内容,[]byte从而避免上述开销。

As an example, if an io.Writeris something that writes to an in-memory buffer (bytes.Bufferis 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 stringinto a []bytewithout converting the stringto []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 stringinto an io.Writer. But it does this by first checking if the (dynamic type of the) passed io.Writerhas a WriteString()method, and if so, that will be used (whose implementation is likely more efficient). If the passed io.Writerhas 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.ResponseWritertoo. And if you look at the implementation of http.ResponseWriter: it's the unexported type http.response(server.gocurrently line #308) which does implement WriteString()(currently line #1212) so it does imply improvement.

您可能认为这WriteString()只会在内存缓冲区的情况下占上风,但事实并非如此。Web 请求的响应也经常被缓冲(使用内存缓冲区),因此在这种情况下它可能会提高性能http.ResponseWriter。如果您查看 : 的实现,http.ResponseWriter它是未导出的类型http.responseserver.go当前第 308 行),它确实实现了WriteString()(当前第 1212 行),因此它确实意味着改进。

All in all, whenever you write stringvalues, 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 stringcreated 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 stringto 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 stringusing the fmtpackage, use fmt.Fprint()which does not require a format string:

如果你只想写一个stringusingfmt包,使用fmt.Fprint()它不需要格式string

fmt.Fprint(w, "Hello")

Another benefit of using the fmtpackage is that you can write values of other types too, not just strings, e.g.

使用该fmt包的另一个好处是您也可以编写其他类型的值,而不仅仅是strings,例如

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 fmtpackage.)

(当然,如何将任何值转换为 astring以及最终转换为一系列字节的规则在fmt包的文档中有明确定义。)

For "simple" formatted outputs the fmtpackage 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.ResponseWriterwhich is an io.Writerto this somethingif it supports writing the result to an io.Writeron-the-fly.

在这种情况下,如果它支持将结果写入即时http.ResponseWriter,则传递/移交您的which is an io.Writerto 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 jsonpackage 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.Encoderby calling json.NewEncoder()to which you can pass your http.ResponseWriteras 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.WriteStringand fmt.Fprintfboth are taking Writeras 1st argument which is also a interface wrapping Write(p []byte) (n int, err error)method

所以 inio.WriteStringfmt.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 ResponseWritervariable 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}