Fazendo requisições HTTP

É muito comum que um programa precise se comunicar com outro, seja para uma integração com outros sistemas ou microsserviços internos.

Embora em alguns casos essa comunicação possa ser feita com gRPC, o mais comum é que elas sejam feita através de uma API (Application Programming Interface) REST.

Nesse post vamos ver como fazer requisições (GET, POST e etc..) e tratar sua resposta.

GET

Para começar, vamos importar 3 packages.

  • io/ioutil para fazer leitura da resposta;
  • log para logar os erros;
  • net/http para executar a requisição.

Com os packages importados, podemos executar nossa requisição e verificar se houve algum erro.

func main() {
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts")
    if err != nil {
        log.Fatalln(err)
    }
}

Validado se houve algum erro na requisição, vamos fazer a leitura da resposta.

func main() {
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts")
    if err != nil {
        log.Fatalln(err)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()
}

Como a função ioutil.ReadAll retorna um array de bytes, nosso próximo passo pode ser converter a variável body para string e exibir no terminal, ou associar à uma struct e então exibir o resultado no terminal.

Como a string seria muito simples, vamos fazer a segunda opção. Para isso, precisamos importar o package encoding/json e criar uma struct que chamaremos de Post.

type Post struct {
    UserID int32  `json:"userId"`
    ID     int32  `json:"id"`
    Title  string `json:"title"`
    Body   string `json:"body"`
}

Voltando para nossa função, vamos criar uma variável para armazenar os posts e utilizar a função json.Unmarshal para popular essa variável como resultado da requisição.

func main() {
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts")
    if err != nil {
        log.Fatalln(err)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()

    var posts []Post
    err = json.Unmarshal(body, &posts)
    if err != nil {
        log.Fatalln(err)
    }
}

Por fim, vamos iterar essa variável e printar seu resultado no terminal.

func main() {
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts")
    if err != nil {
        log.Fatalln(err)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()

    var posts []Post
    err = json.Unmarshal(body, &posts)
    if err != nil {
        log.Fatalln(err)
    }

    for _, post := range posts {
        log.Printf("%+v\n", post)
    }
}

Agora é só fazer um go run main.go e ver o resultado.

Post

Para requisições do tipo POST, vamos importar 5 packages.

  • bytes para criar um objeto que implemente a interface io.Reader;
  • encoding/json para fazer o encode do nosso payload;
  • io/ioutil para fazer leitura da resposta;
  • log para logar os erros;
  • net/http para executar a requisição.

Para começar, vamos criar nosso payload usando um map, fazer seu encode com a função json.Marshal e criar o objeto que implementa a interface io.Reader com a função bytes.NewBuffer.

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)
}

Com o payload pronto, vamos utilizar a função http.Post para fazer a requisição. Essa função espera 3 parâmetros, sendo eles a URL, o content-type e o payload.

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)

    resp, err := http.Post("https://postman-echo.com/post", "application/json", payload)
    if err != nil {
        log.Fatalln(err)
    }
}

Para finalizar, vamos ler a resposta, assim como fizemos para o GET, e printa-la como uma string no terminal.

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)

    resp, err := http.Post("https://postman-echo.com/post", "application/json", payload)
    if err != nil {
        log.Fatalln(err)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()

    log.Printf("%s", body)
}

Agora é só fazer o go run main.go e ver o resultado.

GET, POST, PUT, PATCH, DELETE…

As duas formas de fazer uma requisição mostradas até aqui são bem simples de serem utilizadas, porém são um pouco limitadas, já que, por exemplo, não conseguimos editar nenhum item do cabeçalho da requisição.

Para esses casos temos a função http.NewRequest. Essa função aceita 3 parâmetros, sendo eles o tipo de request (GET, POST, PUT..), a URL e o payload (use nil para quando não precisar enviar nada).

Como exemplo, vamos reescrever o exemplo do POST utilizando a função http.NewRequest.

Primeira coisa que vamos fazer é adicionar uma variável global chamada client. Essa variável vai conter o cliente http responsável pela execução das requisições.

var client = &http.Client{}

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)
}

Agora vamos mudar a chamada da função http.Post para http.NewRequest e, após validar se houve algum erro, adicionar um cabeçalho com o content-type da requisição.

var client = &http.Client{}

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)

    req, err := http.NewRequest(http.MethodPost, "https://postman-echo.com/post", payload)
    if err != nil {
        log.Fatalln(err)
    }
    req.Header.Add("Content-Type", "application/json")
}

Feita essa mudança, vamos executar a requisição com o método (*http.Client).Do.

var client = &http.Client{}

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)

    req, err := http.NewRequest(http.MethodPost, "https://postman-echo.com/post", payload)
    if err != nil {
        log.Fatalln(err)
    }
    req.Header.Add("Content-Type", "application/json")

    resp, err := client.Do(req)
    if err != nil {
        log.Fatalln(err)
    }
}

Após a requisição ter sido a executada, vamos ler a resposta. Essa parte é igual ao exemplo anterior.

var client = &http.Client{}

func main() {
    body, _ := json.Marshal(map[string]string{
        "nome": "Tiago Temporin",
        "idade": 32,
    })
    payload := bytes.NewBuffer(body)

    req, err := http.NewRequest(http.MethodPost, "https://postman-echo.com/post", payload)
    if err != nil {
        log.Fatalln(err)
    }
    req.Header.Add("Content-Type", "application/json")

    resp, err := client.Do(req)
    if err != nil {
        log.Fatalln(err)
    }

    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        log.Fatalln(err)
    }
    defer resp.Body.Close()

    log.Printf("%s", body)
}

Agora é só executar o código com um go run main.go e ver o resultado.

Deixem suas dúvidas nos comentários.

Até a próxima!


Subscreva

Fique por dentro de tudo o que acontece no mundo Go.

Deixe uma resposta