当前位置: 首页 > 面试题库 >

ResponseWriter.Write和io.WriteString有什么区别?

诸葛亮
2023-03-14
问题内容

我已经看到了将内容写入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")

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


问题答案:

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.responseserver.go当前为#308行)可以实现WriteString()(当前为#1212行),因此它确实意味着有所改进。

总之,每当您编写string值时,建议使用io.WriteString()它,因为它可能会更有效(更快)。

fmt.Fprintf()

您应该将其视为一种便捷的方法,为要写入的数据添加更多格式,以换取性能稍差的东西。

因此,fmt.Fprintf()如果要string以简单的方式创建格式,请使用,例如:

name := "Bob"
age := 23
fmt.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())

(当然,如何stringfmt包的文档中定义如何将任何值最终转换为字节以及最终转换为字节序列的规则。)

对于“简单”格式的输出,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.Encoder通过调用json.NewEncoder()来创建新http.ResponseWriterio.Writer,并将用作,然后调用Encoder.Encode(),然后直接将JSON结果写入响应编写器。

这里的一个缺点是,如果生成JSON响应失败,则可能会有部分发送/提交的响应,您将无法收回该响应。如果这对您来说是个问题,那么除了在缓冲区中生成响应之外,您别无选择,并且如果封送处理成功,则可以立即编写完整的响应。



 类似资料:
  • 问题内容: 两者都意味着空间,但是有什么区别吗? 问题答案: 一个是不间断空间,另一个是常规空间。不间断的空格表示该行不应在该点处换行,就像它不会在一个单词的中间换行一样。 此外,正如斯文德(Svend)在其评论中指出的那样,不间断的空间不会崩溃。

  • 本文向大家介绍<%# %> 和 <% %> 有什么区别?相关面试题,主要包含被问及<%# %> 和 <% %> 有什么区别?时的应答技巧和注意事项,需要的朋友参考一下 答:<%# %>表示绑定的数据源 <%%>是服务器端代码块  

  • 问题内容: 以下代码之间有什么区别: 和 Python建议采用一种做事方式,但有时似乎不止一种。 问题答案: 一个是函数调用,一个是文字: 使用第二种形式。它更具Python风格,并且可能更快(因为它不涉及加载和调用单独的函数)。

  • 发展至今(2020 年 6 月份),GCC 编译器已经更新至 10.1.0 版本,其功能也由最初仅能编译 C 语言,扩增至可以编译多种编程语言,其中就包括 C++ 。 除此之外,当下的 GCC 编译器还支持编译 Go、Objective-C,Objective-C ++,Fortran,Ada,D 和 BRIG(HSAIL)等程序,甚至于 GCC 6 以及之前的版本还支持编译 Java 程序。但本

  • 问题内容: 比较PMD和CheckStyle有一个问题。但是,我找不到关于PMD和FindBugs之间差异/相似性的详尽分类。我相信一个关键的区别是PMD适用于源代码,而FindBugs适用于编译的字节码文件。但是就功能而言,这应该是一个选择,还是两者相辅相成? 问题答案: 我同时使用。我认为他们是相辅相成的。 正如您所说,PMD在源代码上工作,因此会发现诸如以下问题:违反命名约定,缺少花括号,放

  • 问题内容: 我被要求在portlet和门户网站上工作。 我想知道portlet和servlet之间的区别吗? Portlet与Servlet有何不同之处(可能在功能上)? 问题答案: Portlet是JSR-168标准的一部分,该标准规范门户容器和组件。这与Web容器(和Servlet)的标准不同。尽管这两个标准之间肯定有很强的相似之处,但是它们在容器,API,生命周期,配置,部署等方面有所不同。