Go로 유형이 안전한 HTTP 요청 보내기

시나리오



Gophers의 경우 기본적으로 클라이언트가 요청할 코드를 작성합니다. 때로는 타사에서 제공하는 RESTful API를 요청해야 합니다. 현재로서는 요청을 모으는 것이 어렵지는 않지만 오류가 발생하기 쉽다고 생각합니다.

예를 들어, 이와 같은 요청을 보내려는 경우 매우 간단해 보이지만 실제로 작성하는 것은 여전히 ​​지루합니다.

POST /articles/5/update?device=ios HTTP/1.1
Host: go-zero.dev
Authorization: Bearer <jwt-token>

{"author": "kevin", "body": "this is not important!", "title": "my title", "type":6}


네이티브 방식으로 이동



이 API는 실제로 매우 간단하며 처음부터 직접 작성할 수 있습니다.

func main() {
    var buf bytes.
    encoder := json.NewEncoder(&buf)
    params := map[string]interface{}{
        "title":  "my title",
        "body":   "this is not important!",
        "author": "kevin",
        "type":   6,
    }
    if err := encoder.Encode(params); err ! = nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }

    url := fmt.Sprintf("http://go-zero.dev/articles/%d/update?device=%s", 5, "ios")
    req, err := http.NewRequest(http.MethodPost, url, &buf)
    if err ! = nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }

    req.Header.Add("Authorization", "Bearer <jwt-token>")
    cli := http.Client{}
    resp, err := cli.Do(req)
    if err ! = nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }

    io.Copy(os.Stdout, resp.Body)
}


테스트를 실행한 결과 200 OK 를 얻지 못하고 패킷을 덤프했으며 요청이 다음과 같이 표시되는 것을 발견했습니다. 실패의 이유를 생각할 수 있습니까?

POST /articles/5/update?device=ios HTTP/1.1
Host: go-zero.dev
User-Agent: Go-http-client/1.1
Content-Length: 79
Authorization: Bearer <jwt-token>
Accept-Encoding: gzip

{"author": "kevin", "body": "this is not important!", "title": "my title", "type":6}


실패에 대한 구체적인 이유는 아래에서 설명하므로 이 코드를 먼저 설명하겠습니다. map[string]interface{}가 스플라이싱 매개변수에 사용되는 것을 볼 수 있습니다. 각 필드에 대해 유형이 일치하는지 확인할 수 없습니다. 서버에서 보내고200 OK 수신해야만 제대로 전달되었는지 확인할 수 있습니다. 예를 들어, type 매개 변수는 여기서 int 유형으로 사용되며 실수로 string 유형으로 작성할 수 있습니다. 하지만 요청하지 않고는 이 매개변수가 잘못 쓰여졌다는 것을 여전히 알아내기 어렵다.

따라서 httpcgo-zero 패키지가 형식 안전을 위해 어떻게 사용되는지 살펴보겠습니다.

httpc 구현


httpc 패키지로 요청하는 코드가 어떻게 작성되었는지 살펴보겠습니다.

const url = "http://go-zero.dev/articles/:id/update"

type UpdateArticle struct {
    ID            int    `path: "id"`
    Device        string `form: "device,options=ios,android,web,desktop"`
    Authorization string `header: "Authorization"`
    Title         string `json: "title"`
    Body          string `json: "body"`
    Author        string `json: "author"`
    Type          int    `json: "type"`
}

func main() {
    data := &UpdateArticle{
        ID:            5,
        Device:        "ios",
        Authorization: "Bearer <jwt-token>",
        Title:         "my title",
        Body:          "this is not important!",
        Author:        "kevin",
        Type:          6,
    }

    resp, err := httpc.Do(context.Background(), http.MethodPost, url, data)
    if err ! = nil {
        fmt.Fprintln(os.Stderr, err)
        return
    }

    io.Copy(os.Stdout, resp.Body)
}


요청을 전송하여 코드를 확인하면 결과는 예상대로입니다.

POST /articles/5/update?device=ios HTTP/1.1
Host: go-zero.dev
User-Agent: Go-http-client/1.1
Content-Length: 79
Content-Type: application/json; charset=utf-8
Authorization: Bearer <jwt-token>
Accept-Encoding: gzip

{"author": "kevin", "body": "this is not important!", "title": "my title", "type":6}


이전 코드와 달리 Content-Type: application/json; charset=utf-8 헤더가 하나 더 설정되어 있고 이전 코드에서 Content-Type 설정을 잊었습니다.
httpc 구현은 요청의 유형을 정의하고 Do로 전송함으로써 매우 이해하기 쉽습니다. 코드에 표시된 대로 path , form , headerjson 를 지원하므로 HTTP 요청을 보내는 것이 매우 쉽고 형식이 안전합니다.

더 많은 기능



위에 표시된 사용 용이성과 유형 안전성 외에도 httpc 패키지에는 다음과 같은 기능이 있습니다.
  • context를 사용한 시간 초과 제어 . 요청에 대해 ctx를 전달할 수 있습니다.
  • OpenTelemetry의 자동 통합 . 서버에서 반환된 trace-id , span-id는 클라이언트와 서버가 함께 작업하여 문제를 조사할 수 있도록 로그에 자동으로 기록됩니다.
  • httpc.Service를 사용하여 회로 차단기 능력을 얻습니다. 서버 측에 문제가 발생하면 불필요한 요청을 피하고 서버 측의 부담을 줄이기 위해 요청 전송을 자동으로 중지합니다.

  • 프로젝트 주소



    https://github.com/zeromicro/go-zero
    go-zero 사용을 환영하고 별표를 표시하여 지원하세요!

    좋은 웹페이지 즐겨찾기