golang (*bufio.Reader)的读取方法
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了golang (*bufio.Reader)的读取方法相关的知识,希望对你有一定的参考价值。
参考技术A bufReader.ReadBytes('\n')和 bufReader.ReadString('\n')在读到文件最后一行时,会同时返回内容line和io.EOF。而bufReader.Read()读取到末尾时,会先返回内容,然后再下一次迭代时才返回io.EOF从 bufio.Reader 创建请求
【中文标题】从 bufio.Reader 创建请求【英文标题】:creating a request from bufio.Reader 【发布时间】:2022-01-21 23:45:39 【问题描述】:我正在尝试实现一个接受多部分混合的批处理处理程序。
我目前有点幼稚的实现如下所示。稍后我将尝试汇总响应并发送多部分响应。
我当前的问题是我无法将各个部分的正文解析为新请求。
func handleBatchPost(w http.ResponseWriter, r *http.Request)
// read the multipart body
reader, err := r.MultipartReader()
if err != nil
http.Error(w, fmt.Sprintf("could not read multipart %v\n", err), http.StatusBadRequest)
// read each part
for
part, err := reader.NextPart()
if err == io.EOF
break
else if err != nil
http.Error(w, fmt.Sprintf("could not read next part %v\n", err), http.StatusBadRequest)
return
// check if content type is http
if part.Header.Get("Content-Type") != "application/http"
http.Error(w, fmt.Sprintf("part has wrong content type: %s\n", part.Header.Get("Content-Type")), http.StatusBadRequest)
return
// parse the body of the part into a request
req, err := http.ReadRequest(bufio.NewReader(part))
if err != nil
http.Error(w, fmt.Sprintf("could not create request: %s\n", err), http.StatusBadRequest)
return
// handle the request
router.ServeHTTP(w, req)
func handleItemPost(w http.ResponseWriter, r *http.Request)
var item map[string]interface
if err := json.NewDecoder(r.Body).Decode(&item); err != nil
http.Error(w, fmt.Sprintf("could not decode item json: %v\n", err), http.StatusBadRequest)
return
w.Write([]byte(`"success": true`))
我收到来自服务器的错误响应。 ReadRequest
似乎没有读取正文,而只是读取标题(方法、url 等)。
could not decode item json: EOF
这是我要发送的有效负载。
POST /batch HTTP/1.1
Host: localhost:8080
Content-Type: multipart/mixed; boundary=boundary
--boundary
Content-Type: application/http
Content-ID: <item1>
POST /items HTTP/1.1
Content-Type: application/json
"name": "batch1", "description": "batch1 description"
--boundary
Content-Type: application/http
Content-ID: <item2>
POST /items HTTP/1.1
Content-Type: application/json
"name": "batch2", "description": "batch2 description"
--boundary--
我在 gmail api 文档 https://developers.google.com/gmail/api/guides/batch 上发现了这种模式。
【问题讨论】:
【参考方案1】:主要问题是您的有效负载没有为子请求指定Content-Length
标头。如果缺少 Content-Length
标头,http.ReadRequest()
将假定没有正文,不会读取和呈现实际正文,这就是您收到 EOF 错误的原因。
所以首先提供缺少的Content-Length
标头:
POST /batch HTTP/1.1
Host: localhost:8080
Content-Type: multipart/mixed; boundary=boundary
--boundary
Content-Type: application/http
Content-ID: <item1>
POST /items HTTP/1.1
Content-Type: application/json
Content-length: 58
"name": "batch1", "description": "batch1 description"
--boundary
Content-Type: application/http
Content-ID: <item2>
POST /items HTTP/1.1
Content-Type: application/json
Content-length: 58
"name": "batch2", "description": "batch2 description"
--boundary--
这样它应该可以工作,但请注意,由于您在同一循环中处理部分,并最终调用router.ServeHTTP(w, req)
,因此您重用了w
编写器。这是什么意思?如果handleItemPost()
向输出中写入任何内容,随后对handleItemPost()
的调用将无法收回。
例如如果 handleItemPost()
失败,它会以 HTTP 错误响应(这意味着设置响应状态并写入正文)。随后的handleItemPost()
不能再次报告错误(标头已提交),并且如果它报告成功,则错误标头已经发送,只能将进一步的消息写入错误正文。
例如,如果我们将handleItemPost()
修改为:
func handleItemPost(w http.ResponseWriter, r *http.Request)
var item map[string]interface
if err := json.NewDecoder(r.Body).Decode(&item); err != nil
fmt.Printf("JSON decode error: %v\n", err)
return
fmt.Printf("Success, item: %v\n", item)
并执行以下curl
命令:
curl localhost:8080/batch -X POST \
-H "Content-Type: multipart/mixed; boundary=boundary" \
-d '--boundary
Content-Type: application/http
Content-ID: <item1>
POST /items HTTP/1.1
Content-Type: application/json
Content-length: 58
"name": "batch1", "description": "batch1 description"
--boundary
Content-Type: application/http
Content-ID: <item2>
POST /items HTTP/1.1
Content-Type: application/json
Content-length: 58
"name": "batch2", "description": "batch2 description"
--boundary--'
我们将看到以下输出:
Success, item: map[description:batch1 description name:batch1]
Success, item: map[description:batch2 description name:batch2]
请注意,如果 handleItemPost()
需要保持完整的功能并可自行调用(以处理请求并产生响应),则不能对其所有调用使用相同的 http.ResponseWriter
。
在这种情况下,您可以为每个调用创建和使用单独的http.ResponseWriter
。标准库有一个实现http.ResponseWriter
的httptest.ResponseRecorder
类型。它主要用于测试目的,但您也可以在这里使用它。它记录了书面答复,因此您可以在通话后进行检查。
例如:
w2 := httptest.NewRecorder()
router.ServeHTTP(w2, req)
if w2.Code != http.StatusOK
fmt.Printf("handleItemPost returned non-OK status: %v\n", w2.Code)
fmt.Printf("\terror body: %v\n", w2.Body.String())
使用您的原始请求运行此程序(不指定 Content-Length
),输出将是:
handleItemPost returned non-OK status: 400
error body: could not decode item json: EOF
handleItemPost returned non-OK status: 400
error body: could not decode item json: EOF
但是当您指定子请求的Content-Length
时,不会打印输出(错误)。
【讨论】:
以上是关于golang (*bufio.Reader)的读取方法的主要内容,如果未能解决你的问题,请参考以下文章