ResponseWriter.Write和io.WriteString有什么区别? [英] What's the difference between ResponseWriter.Write and io.WriteString?

查看:117
本文介绍了ResponseWriter.Write和io.WriteString有什么区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  func Handler(w http.ResponseWriter,req * http.Request){
io.WriteString(w,blabla.\\\

}

和:

pre $ func Handler(w http.ResponseWriter,r * http.Request){
w.Write([] byte(blabla\\\
))
}

还有:

  fmt.Fprintf(w,blabla)

它们之间有什么区别?哪一个是最好用的?

解决方案

io.Writer



输出流代表可以写入字节序列的目标。在Go中,这由一般的 io.Writer 接口:

  type Writer接口{
Write(p [] byte)(n int,err错误)
}

所有包含 Write() 方法可用作输出,例如磁盘上的文件( os.File ),网络连接( net.Conn )或内存缓冲区( bytes.Buffer )。 $ b

http.ResponseWriter ,用于配置HTTP响应和发送数据到客户端也是这样的 io.Writer ,您要发送的数据(响应bod y)通过调用(不一定只是一次) ResponseWriter.Write()(这是为了实现一般的 io.Writer )。这是您实现 http.ResponseWriter 界面的唯一保证。



WriteString()



现在转到 WriteString()。通常我们希望将文本数据写入 io.Writer 。是的,我们可以通过简单地将字符串转换为 []字节来实现,例如

  w.Write([] byte(Hello))

按预期工作。然而,这是一个非常频繁的操作,因此对于这个被 io.stringWriter 未导出接口捕获的一般接受方法:

 类型stringWriter接口{
WriteString(s字符串)(n int,err错误)
}

这个方法可以写一个字符串值而不是 []字节。因此,如果某些东西(也实现 io.Writer )实现此方法,则可以简单地传递 string 值而不 [] byte 转换。 字符串转换为 [] byte code>必须复制字符串内容(因为在Go中 string 值是不可变的),所以如果字符串为更大并且/或者您必须多次执行此操作,则会有一些开销。



根据 io.Writer 的性质和实现细节,可能会写入字符串的内容,而无需将其转换为 [] byte ,从而避免上述开销。



例如,如果 io.Writer 是写入内存缓冲区的内容( bytes.Buffer 这样的例子),它可以利用内建的 copy() 功能:


复制内置函数cop将来自源片段的元素转换为目标片段。 (作为一种特殊情况,它也会将字节中的字节复制到一个字节片段中。)


copy()可用于将字符串的内容(字节)复制到 [] byte ,而不将 / b>

  buf:= make([] byte,100)
copy(buf,Hello)

现在有一个实用程序功能 io.WriteString() 字符串写入 io.Writer 。但它通过首先检查(动态类型)是否通过 io.Writer 具有 WriteString()方法,如果是这样,那么将会使用(其实现可能更有效)。如果传递的 io.Writer 没有这样的方法,那么通用的转换为字节的slice-and-write-it-that-way 方法将被用作后备。



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

总之,当你写 string 值时,建议使用 io.WriteString(),因为它可能更高效(更快) 。



fmt.Fprintf()



您应该将这看作是一种方便而简单的方式,以便为要写入的数据添加更多的格式,以换取较低的性能。

因此,使用 fmt.Fprintf() 如果你想格式化<
$ b

  name:=Bob  string  
年龄:= 23
fmt.Fprintf(w ,我的名字是%s,我是%d岁,姓名,年龄)

这将导致以下字符串被写入:

 我叫鲍勃,今年23岁。 

有一点你不能忘记: fmt.Fprintf()需要一个格式字符串,所以它将被预处理,而不是按原样写入输出。作为一个简单的例子:

  fmt.Fprintf(w,100 %%)

你会期望100 %%会写入到输出中与2 字符),但只有一个将被发送格式字符串是一个特殊字符和 %% 只会在输出中产生一个。



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

  fmt.Fprint(w,Hello)

使用 fmt 包是你可以写其他类型的值,而不只是 string s,例如

  fmt.Fprint(w,23,time.Now())

(当然了在 fmt 字符串并最终定义为一系列字节)

对于简单格式化输出, fmt 包可能没问题。对于复杂的输出文档,请考虑使用 text / template (用于一般文字)和 html / template

传递/移交 http.ResponseWriter



为了保持完整性,我们应该提及,通常情况下,您希望作为网络响应发送的内容是由支持流式传输结果的某些东西生成的。一个例子可能是一个JSON响应,它是从结构或映射生成的。



在这种情况下,通过/移交 http.ResponseWriter 这是 io.Writer 这个 something 如果它支持将结果写入 io.Writer on-the-fly。



一个很好的例子就是生成JSON响应。当然,你可以使用 json.Marshal() ,它返回一个字节片段,您可以通过调用 ResponseWriter.Write()来发送字节片段。



然而,让 json 包知道你有一个 io.Writer ,最终你想把结果发送给它。这样就不必先在缓冲区中生成JSON文本,然后将其写入响应中然后丢弃。您可以创建一个新的 json.Encoder 通过调用 json.NewEncoder() ,您可以将 http.ResponseWriter 作为 io.Writer 传递给它,并调用 Encoder.Encode() 之后会直接将JSON结果写入响应编写器。



这里的一个缺点是,如果生成JSON响应失败,那么可能会有一个部分发送/你不能收回。如果这对你来说是一个问题,除了在缓冲区中生成响应之外,你没有其他选择,如果编组成功,那么你可以立即写完整的响应。


I've seen three ways of writing content to HTTP response:

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?

解决方案

io.Writer

An output stream represents a target to which you can write sequence of bytes. In Go this is captured by the general io.Writer interface:

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

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.

WriteString()

Now on to WriteString(). Often we want to write textual data to an io.Writer. Yes, we can do that by simply converting the string to a []byte, e.g.

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 unexported interface:

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), so there is some overhead which becomes noticeable if the string is "bigger" and/or you have to do this many times.

Depending on the nature and implementation details of an io.Writer, it may be that it is possible to write the content of a string without converting it to []byte and thus avoiding the above mentioned overhead.

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:

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

The copy() may be used to copy the content (bytes) of a string into a []byte without converting the string to []byte, e.g.:

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-it-that-way method will be used as a "fallback".

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 mean an improvement.

All in all, whenever you write string values, recommended to use io.WriteString() as it may be more efficient (faster).

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.:

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:

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(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.

If you just want to write a string using the fmt package, use fmt.Fprint() which does not require a format string:

fmt.Fprint(w, "Hello")

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

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

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

Passing / handing over 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.

In such cases it's often more efficient to pass / hand over your http.ResponseWriter which is an io.Writer to this something if it supports writing the result to an io.Writer on-the-fly.

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

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.

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.

这篇关于ResponseWriter.Write和io.WriteString有什么区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆