猿问

ResponseWriter.Write 和 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")

他们之间有什么区别?首选使用哪一个?


萧十郎
浏览 548回答 2
2回答

慕斯709654

io.Writer输出流表示您可以向其写入字节序列的目标。io.Writer在 Go 中,这是由通用接口捕获的: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为 a来做到这一点[]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转换。这似乎是对代码的轻微简化,但不仅如此。将 a 转换string为[]byte必须复制string内容(因为string值在 Go 中是不可变的,请在此处阅读有关它的更多信息:golang: []byte(string) vs []byte(*string)),因此会产生一些开销如果string“更大”和/或您必须多次执行此操作,则很明显。根据 a 的性质和实现细节,io.Writer可以编写 a 的内容string而不将其转换为[]byte,从而避免上述开销。例如,如果 anio.Writer是写入内存缓冲区的东西(bytes.Buffer就是这样一个例子),它可以利用内置copy()函数:复制内置函数将元素从源切片复制到目标切片。(作为一种特殊情况,它还将字节从字符串复制到字节片。)可copy()用于将 a 的内容(字节)复制string到 a[]byte而无需将 转换string为[]byte,例如:buf := make([]byte, 100)copy(buf, "Hello")现在有一个“实用程序”函数io.WriteString()可以将 astring写入io.Writer. 但它首先检查传递的(动态类型)io.Writer是否有WriteString()方法,如果有,将使用该方法(其实现可能更有效)。如果传递io.Writer的没有这样的方法,那么一般的convert-to-byte-slice-and-write-that方法将被用作“后备”。您可能认为这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.Writerio.Writer一个很好的例子是生成 JSON 响应。当然,您可以使用 将一个对象编组为 JSON json.Marshal(),它会返回一个字节切片,您可以通过调用简单地发送它ResponseWriter.Write()。然而,让json包知道你有一个io.Writer,并且最终你想将结果发送给它会更有效。这样就不必首先在缓冲区中生成 JSON 文本,您只需将其写入响应然后丢弃。json.Encoder您可以通过调用来创建一个新的json.NewEncoder(),您可以将您的http.ResponseWriteras传递给它io.Writer,然后调用Encoder.Encode()将直接将 JSON 结果写入您的响应编写器。这里的一个缺点是,如果生成 JSON 响应失败,您可能会收到部分发送/提交的响应,您无法收回。如果这对您来说是个问题,那么您除了在缓冲区中生成响应之外别无选择,如果封送处理成功,那么您可以立即编写完整的响应。

慕田峪7331174

从这里(ResponseWriter)可以看出,它是一个带有Write([]byte) (int, error)方法的接口。所以 inio.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") 时,请检查此处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")在这里检查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}您只是ResponseWriter在两种方法中传递变量时间接调用 Write Method。所以为什么不直接使用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}
随时随地看视频慕课网APP

相关分类

Go
我要回答