为什么在golang的Web处理程序中,“写入”文件后面的消息没有显示出来?

huangapple go评论83阅读模式
英文:

Why are the messages that follow after "writing" the file inside golang web handlers not displayed?

问题

我正在使用golang的web服务器和处理程序,并遇到了一个小问题。我想要提供一个包含以下文本的文件。问题是消息没有显示出来。以下是处理程序的代码:

mux := http.NewServeMux()

mux.HandleFunc("/myfile", func(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, "./ui/static/js/main.js")
	w.Write([]byte("Message after (1)"))
	w.Write([]byte("Message after (2)"))
	fmt.Println("Console message!")
})

http.ListenAndServe(":4000", mux)

在浏览器中的输出结果是:

var navLinks = document.querySelectorAll("nav a");
for (var i = 0; i < navLinks.length; i++) {
	var link = navLinks[i]
	if (link.getAttribute('href') == window.location.pathname) {
		link.classList.add("live");
		break;
	}
}

但是,当我在文件之前写入一条消息(或多条消息)时,它会显示文件之前和之后的所有消息:

mux := http.NewServeMux()

mux.HandleFunc("/myfile", func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Message 1 (before)"))
	http.ServeFile(w, r, "./ui/static/js/main.js")
	w.Write([]byte("Message 1 (after)"))
	w.Write([]byte("Message 2 (after)"))
	fmt.Println("Console message!")
})

http.ListenAndServe(":4000", mux)

在浏览器中的输出结果是:

Message 1 (before)var navLinks = document.querySelectorAll("nav a");
for (var i = 0; i < navLinks.length; i++) {
	var link = navLinks[i]
	if (link.getAttribute('href') == window.location.pathname) {
		link.classList.add("live");
		break;
	}
}Message 1 (after)Message 2 (after)

有人知道为什么会出现这种情况以及如何解决吗?换句话说,为什么在文件之前没有消息时,文件之后的消息不显示?

fmt.Println("Console message!") 总是在控制台中显示。

英文:

I'm working with golang web server and handlers and ran into a little problem. I want to serve one file with the following text. The problem is that the messages are not displayed. Here is the handler code:

mux := http.NewServeMux()

mux.HandleFunc(&quot;/myfile&quot;, func(w http.ResponseWriter, r *http.Request) {
	http.ServeFile(w, r, &quot;./ui/static/js/main.js&quot;)
	w.Write([]byte(&quot;Message after (1)&quot;))
	w.Write([]byte(&quot;Message after (2)&quot;))
	fmt.Println(&quot;Console message!&quot;)
})

http.ListenAndServe(&quot;:4000&quot;, mux)

The output in the browser is:

var navLinks = document.querySelectorAll(&quot;nav a&quot;);
for (var i = 0; i &lt; navLinks.length; i++) {
	var link = navLinks[i]
	if (link.getAttribute(&#39;href&#39;) == window.location.pathname) {
		link.classList.add(&quot;live&quot;);
		break;
	}
}

But when I write one message (or more) before the file, it shows both the messages after and before the file:

mux := http.NewServeMux()

mux.HandleFunc(&quot;/myfile&quot;, func(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte(&quot;Message 1 (before)&quot;))
	http.ServeFile(w, r, &quot;./ui/static/js/main.js&quot;)
	w.Write([]byte(&quot;Message 1 (after)&quot;))
	w.Write([]byte(&quot;Message 2 (after)&quot;))
	fmt.Println(&quot;Console message!&quot;)
})

http.ListenAndServe(&quot;:4000&quot;, mux)

The output in the browser is:

Message 1 (before)var navLinks = document.querySelectorAll(&quot;nav a&quot;);
for (var i = 0; i &lt; navLinks.length; i++) {
	var link = navLinks[i]
	if (link.getAttribute(&#39;href&#39;) == window.location.pathname) {
		link.classList.add(&quot;live&quot;);
		break;
	}
}Message 1 (after)Message 2 (after)

Does anyone know why this happens and how to fix it? In other words, why are messages after the file not displayed when there is no message before the file?

fmt.Println(&quot;Console message!&quot;) is always displayed in the console.

答案1

得分: 2

net/http服务器在处理响应体时有三种情况:

  • 如果应用在第一次调用write之前设置了响应内容长度头部(response content length header),那么服务器将使用应用指定的内容长度头部进行标识编码(identity encoding)。
  • 如果响应体适合于固定大小的缓冲区(目前为2048字节),服务器将设置内容长度头部为实际内容长度,并使用标识编码。
  • 否则,服务器将使用分块编码(chunked encoding),并且没有内容长度响应头部。

上述内容指的是服务器写入网络的内容。服务器不会使用这些值更新响应写入器(response writer)的头部。

第一个例子:

调用http.ServeFile会在第一次调用Write之前将响应内容长度头部设置为文件的大小。客户端会读取由内容长度头部指定的字节数。

内容长度不包括两个消息的大小。这些消息会被客户端忽略,或者这两个消息会导致网络连接上的下一个HTTP事务出现错误。

第二个例子(在调用ServeFile之前调用Write):

在第一次调用Write之后,对响应头部的修改将被忽略。ServeFile函数会设置响应内容长度头部为文件的大小,但该值会被忽略。

客户端会读取所有的消息和文件,因为服务器要么使用了分块编码,要么将内容长度头部设置为文件的实际大小。

一些ServeFile的特性可能会正常工作,因为ServeFile无法设置响应头部或响应状态码。

修复方法:

在处理程序中打开文件并将其复制到响应中。不能使用http.ServeFile。

英文:

The net/http server does one of thee things with regards to framing the response body:

  • If the application sets the response content length header before the first call to write, then the server uses identity encoding with the application specified content length header.
  • If the response body fits in fixed size buffer (currently 2048 bytes), the server sets the content length header to the actual content length and uses identity encoding.
  • Otherwise, the server uses chunked encoding with no content length response header.

The above refers to what the server writes to the network. The server does not update the response writer header with these values.

First example:

The call to http.ServeFile sets the response content length header to the size of the file before the first call to Write. The client reads the number of bytes specified by the content length header.

The content length does not include the size of two messages. These messages are ignored by the client or the two messages cause an error the next HTTP transaction on the network connection.

Second example (call to Write before ServeFile):

Modifications to the response header are ignored after the first call to Write. The ServeFile function sets the response content length header to the size of the file, but that value is ignored.

The client reads the all of the messages and file because the server either used chunked encoding or set the content length header to the actual size of the file.

Some ServeFile features may work correctly because ServeFile cannot set the response headers or response status code.

Fix:

Open the file in your handler and copy to the response. You cannot use http.ServeFile.

答案2

得分: 1

http.ServeFile尝试设置Content-Length头部(以及一些其他对于提供静态文件很有用的头部,比如Last-Modified)。由于它将Content-Length设置为文件的长度,浏览器在文件的最后一个字节之后停止监听。如果你执行w.Write([]byte("Message 1 (before)")),这会跳过响应体部分,使得http.ServeFile无法设置任何头部。

英文:

http.ServeFile tries to set the Content-Length header (along with a bunch of other headers that are good for serving static files, like Last-Modified). Since it sets Content-Length to the length of the file, the browser quits listening to you after the last byte of the file. If you do w.Write([]byte(&quot;Message 1 (before)&quot;)) this jumps ahead to the response body part and makes it impossible for http.ServeFile to set any headers.

huangapple
  • 本文由 发表于 2023年1月15日 12:29:35
  • 转载请务必保留本文链接:https://go.coder-hub.com/75122930.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定