gohttp-headers

go http server header Content-type set to multipart/form-data but get Content-Type: text/plain at the client


the go server set the header Content-type to multipart/form-data

router.HandleFunc("/certificates", serveFilesHandler).Methods("GET")

func serveFilesHandler(w http.ResponseWriter, r *http.Request) {

    currentDir, err := os.Getwd()
    if err != nil {
        log.Fatal("Can not find the current directory: ", err)
    }
    pathToCertifs := "../certificates"

    // Create a multipart writer for the response
    multipartWriter := multipart.NewWriter(w)

    files := []string{"client.key", "server.key", "rootCA.key"}

    for _, filename := range files {
        filePath := filepath.Join(currentDir, pathToCertifs, filename)

        fmt.Println("see the filePath: ", filePath)

        // Open the file
        file, err := os.Open(filePath)
        if err != nil {}
        defer file.Close()

        // Create a new form file part
        part, err := multipartWriter.CreateFormFile("files", filename)
        if err != nil {}

        // Copy the file content to the part
        _, err = io.Copy(part, file)
        if err != nil {}
    }

    // Set the content type for the response
    w.Header().Set("Content-Type", multipartWriter.FormDataContentType())
    fmt.Println("Content-Type set to:", w.Header().Get("Content-Type"))
    // printout Content-Type set to: multipart/form-data; boundary=7b326

    // Close the multipart writer
    multipartWriter.Close()
}

but on the client side I got

Expected multipart response, but received: text/plain; charset=utf-8

however the payload is in the body

body, err := ioutil.ReadAll(resp.Body)
    if err != nil {}
Content-Type: text/plain; charset=utf-8
Response Body:
--aee406774ba6a054d52e39a3cdb72f42d32bd30828adbfb1982d278cab56
Content-Disposition: form-data; name="files"; filename="client.key"
Content-Type: application/octet-stream

-----BEGIN PRIVATE KEY-----
MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDNg4ZaTLC/GdLK
xzFDIyPlYyKs/hUXpPkZAQk+3gnvmBaDuMNq2jd2nQoQohmk1zIuD8oj9se5L+3P

but I can't get it by part as the Content-type is not multipart/form-data, so that does not work

multipartReader := multipart.NewReader(resp.Body, boundaryFromContentType(contentType))

    // Read each part
    for {
        part, err := multipartReader.NextPart()
        if err != nil {
            break
        }
        defer part.Close()
......

What am i missing, thank you ?

ps: more details are asked to post this question, I feel it is pretty clear, so I am adding this line, it may work after that.


Solution

  • HTTP response headers must be set prior to writing anything to the response body. Once headers are committed (they are when you write something to the response body), you cannot set or change the headers.

    You create your multipart writer and all the parts and contents, then you set the response header, and after that you only close the multipart writer. The close is only to finish the multipart message and write the trailing boundary, but a lot of its content may already be written and committed.

    Move setting the header(s) before adding / writing anything to the multipart writer:

    // Create a multipart writer for the response
    multipartWriter := multipart.NewWriter(w)
    
    w.Header().Set("Content-Type", multipartWriter.FormDataContentType())
    
    // Now proceed to add files...