我正在嘗試實作一個接受多部分混合的批處理程式。
我目前有點幼稚的實作如下所示。稍后我將嘗試聚合回應并發送多部分回應。
我當前的問題是我無法將各個部分的正文決議為新請求。
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不是在閱讀正文,而只是在閱讀標題(方法、網址等)。
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上發現了這種模式。
uj5u.com熱心網友回復:
主要問題是您的有效負載沒有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)在最后呼叫,因此您正在重用wwriter。這是什么意思?如果handleItemPost()向輸出寫入任何內容,后續呼叫將handleItemPost()無法收回。
例如,如果 ahandleItemPost()失敗,它會以 HTTP 錯誤回應(這意味著設定回應狀態并寫入正文)。后續handleItemPost()不能再次報錯(headers 已經提交),而且如果它會報錯,則錯誤 header 已經發送,只能向錯誤正文寫入進一步的訊息。
例如,如果我們修改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為每個呼叫創建和使用單獨的。標準庫有一個httptest.ResponseRecorder實作http.ResponseWriter. 它主要用于測驗目的,但您也可以在這里使用它。它記錄了書面答復,因此您可以在通話后進行檢查。
例如:
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子請求的 時,不會列印輸出(錯誤)。
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/387986.html
