robert-min / handson-go

Go-lang hands on guide
0 stars 0 forks source link

Chapter3. HTTP client - Send data, Binary data #9

Open robert-min opened 1 year ago

robert-min commented 1 year ago

데이터 송신

robert-min commented 1 year ago

데이터 전송 기능 구현

type pkgData struct {
    Name    string `json:"name"`
    Version string `json:"version"`
}

type pkgRegisterResult struct {
    ID string `json:"id"`
}

func registerPackageData(url string, data pkgData) (pkgRegisterResult, error) {
    p := pkgRegisterResult{}
    b, err := json.Marshal(data)
    if err != nil {
        return p, err
    }
    reader := bytes.NewReader(b)
        // url에 byte 정보를 받아서 POST 요청 
    r, err := http.Post(url, "application/json", reader)
    if err != nil {
        return p, err
    }
    defer r.Body.Close()
    respData, err := io.ReadAll(r.Body)
    if err != nil {
        return p, err
    }
    if r.StatusCode != http.StatusOK {
        return p, errors.New(string(respData))
    }
    err = json.Unmarshal(respData, &p)
    return p, err

}
robert-min commented 1 year ago

데이터 전송 기능 테스트 코드

package pkgregister

import (
    "encoding/json"
    "fmt"
    "io"
    "net/http"
    "net/http/httptest"
    "testing"
)

// POST request handler
func packageRegHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        p := pkgData{}

        // Package registration response
        d := pkgRegisterResult{}
        defer r.Body.Close()
        data, err := io.ReadAll(r.Body)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        // Convert JSON data included in POST request to pkg
        err = json.Unmarshal(data, &p)
        if err != nil || len(p.Name) == 0 || len(p.Version) == 0 {
            // HTTP 400
            http.Error(w, "Bad Request", http.StatusBadRequest)
            return
        }
        d.ID = p.Name + "-" + p.Version
        jsonData, err := json.Marshal(d)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprint(w, string(jsonData))
    } else {
        http.Error(w, "Invalid HTTP method specified", http.StatusMethodNotAllowed)
        return
    }
}

func startTestPackageServer() *httptest.Server {
    ts := httptest.NewServer(http.HandlerFunc(packageRegHandler))
    return ts
}

func TestRegisterPackageData(t *testing.T) {
    ts := startTestPackageServer()
    defer ts.Close()
    p := pkgData{
        Name:    "mypackage",
        Version: "0.1",
    }
    resp, err := registerPackageData(ts.URL, p)
    if err != nil {
        t.Fatal(err)
    }
    if resp.ID != "mypackage-0.1" {
        t.Errorf("Expected package id to be mypackage-0.1, Got: %s", resp.ID)
    }
}

func TestRegisterEmptyPacakgeData(t *testing.T) {
    ts := startTestPackageServer()
    defer ts.Close()
    p := pkgData{}
    resp, err := registerPackageData(ts.URL, p)
    if err == nil {
        t.Fatal("Expected error to be non-nil, got nil")
    }
    if len(resp.ID) != 0 {
        t.Errorf("Expected package ID to be empty, got: %s", resp.ID)
    }
}
robert-min commented 1 year ago

Binary DATA 처리

robert-min commented 1 year ago

multipart 타입 데이터 처리를 위한 함수

func createMultiPartMessage(data pkgData) ([]byte, string, error) {
    var b bytes.Buffer
    var err error
    var fw io.Writer

    mw := multipart.NewWriter(&b)

    // Set form field and assign object
    fw, err = mw.CreateFormField("name")
    if err != nil {
        return nil, "", err
    }
    fmt.Fprintf(fw, data.Name)

    fw, err = mw.CreateFormField("version")
    if err != nil {
        return nil, "", err
    }
    fmt.Fprintf(fw, data.Version)

    // Create binary fields and assign "fw"
    fw, err = mw.CreateFormFile("filedata", data.Filename)
    if err != nil {
        return nil, "", err
    }

    _, err = io.Copy(fw, data.Bytes)
    err = mw.Close()
    if err != nil {
        return nil, "", err
    }

    contentType := mw.FormDataContentType()
    return b.Bytes(), contentType, nil
}
robert-min commented 1 year ago

multipart 함수로 처리된 값 POST 요청 함수


func registerPackageData(url string, data pkgData) (pkgRegisterResult, error) {
    p := pkgRegisterResult{}

    // Create multipart/form-data payload
    payload, contentType, err := createMultiPartMessage(data)
    if err != nil {
        return p, err
    }
    reader := bytes.NewReader(payload)
    r, err := http.Post(url, contentType, reader)
    if err != nil {
        return p, err
    }
    defer r.Body.Close()

    respData, err := io.ReadAll(r.Body)
    if err != nil {
        return p, err
    }
    err = json.Unmarshal(respData, &p)
    return p, err
}
robert-min commented 1 year ago

Multipart 타입 함수 테스트 코드


func packageRegHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method == "POST" {
        d := pkgRegisterResult{}
        // 5000 : maximum number of bytes to buffer in memory.
        err := r.ParseMultipartForm(5000)
        if err != nil {
            http.Error(w, err.Error(), http.StatusBadRequest)
        }
        mForm := r.MultipartForm
        // Get file data
        f := mForm.File["filedata"][0]

        // Construct an artificial package ID to return
        d.ID = fmt.Sprintf("%s-%s", mForm.Value["name"][0], mForm.Value["version"][0])
        d.Filename = f.Filename
        d.Size = f.Size

        // Marshal outgoing package registration response
        jsonData, err := json.Marshal(d)
        if err != nil {
            http.Error(w, err.Error(), http.StatusInternalServerError)
            return
        }
        w.Header().Set("Content-Type", "application/json")
        fmt.Fprintf(w, string(jsonData))
    } else {
        http.Error(w, "Invalid HTTP method specified", http.StatusMethodNotAllowed)
        return
    }
}

func startTestPackageServer() *httptest.Server {
    ts := httptest.NewServer(http.HandlerFunc(packageRegHandler))
    return ts
}

func TestRegisterPackageData(t *testing.T) {
    ts := startTestPackageServer()
    defer ts.Close()

    p := pkgData{
        Name:     "mypackage",
        Version:  "0.1",
        Filename: "mypackage-0.1.tar.gz",
        Bytes:    strings.NewReader("data"),
    }
    pResult, err := registerPackageData(ts.URL, p)
    if err != nil {
        t.Fatal(err)
    }

    if pResult.ID != fmt.Sprintf("%s-%s", p.Name, p.Version) {
        t.Errorf("Expected package ID to be %s-%s, Got: %s", p.Name, p.Version, pResult.ID)
    }
    if pResult.Filename != p.Filename {
        t.Errorf("Expected package filename to be %s, Got: %s", p.Filename, pResult.Filename)
    }
    if pResult.Size != 4 {
        t.Errorf("Expected package size to be 4, Got: %d", pResult.Size)
    }
}