Photo by Fabian Irsara on Unsplash
How to build a very simple CLI program to make HTTP requests with Go
Recently I was learning how to build an API with Go for the first time, and when I wanted to test the POST handler, I didn't know how to do it.
So I built a program to help me know how my API behaves when I make a GET request or POST request.
This program only uses the standard library.
First, create a go file named 'get.go' where it contains the function to make GET requests.
package main
import (
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
func GetUrl(url string) string {
response, err := http.Get(url)
if err != nil {
fmt.Print(err.Error())
os.Exit(1)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
return string(responseData)
}
This function takes an URL as a string, then we use the "Get" function from the "net/http" library to get the data from the URL. We use the "ReadAll" function from "io/ioutil" library to get the body from the response variable and stored it in another variable(responseData). We convert to a string the information from "responseData" and that is what "GetUrl" returns it.
Next, create a file as "post.go" where we write a function named "Poster", it has two parameters. We pass an URL and the JSON we want to post as an argument and it returns the JSON if the action is successful.
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
)
func Poster(url string, postString string) string {
var jsonMap map[string]interface{}
json.Unmarshal([]byte(postString), &jsonMap)
postBody, _ := json.Marshal(jsonMap)
responseBody := bytes.NewBuffer(postBody)
response, err := http.Post(url, "aplication/json", responseBody)
if err != nil {
fmt.Print(err.Error())
os.Exit(1)
}
responseData, err := ioutil.ReadAll(response.Body)
if err != nil {
log.Fatal(err)
}
sb := string(responseData)
return sb
}
In "Poster" first, we make a map, then we pass the json as a [] byte as the first argument in "json.Unmarshal" and stored it in the map we just make. Then we encode the JSON map with "json.Marshal", after that we need to pass the body to "bytes.NewBuffer" and stored it in a variable to be used as an argument in the Post method.
We read the response and return it as a string. I did this to be sure that the JSON has been posted.
Finally, create the "main.go". Here we write our commands using the flag package.
package main
import (
"bufio"
"flag"
"fmt"
"os"
)
func main() {
getCmd := flag.NewFlagSet("get", flag.ExitOnError)
url := getCmd.String("url", "No url", "Api Url")
postCmd := flag.NewFlagSet("post", flag.ExitOnError)
postUrl := postCmd.String("url", "", "port or API url")
reader := bufio.NewReader(os.Stdin)
flag.Parse()
if len(os.Args) < 2 {
fmt.Println("expected 'get' or 'post' subcommands")
os.Exit(1)
}
switch os.Args[1] {
case "get":
HandleGet(getCmd, url)
response := HandleGet(getCmd, url)
fmt.Println(response)
case "post":
fmt.Println("Enter body:")
input, err := reader.ReadString('\n')
if err != nil {
fmt.Println("An error occured while reading input. Please try again", err)
return
}
fmt.Println("This is the body that has been posted:")
fmt.Println(HandlePost(postCmd, postUrl, input))
default:
}
}
func HandleGet(getCmd *flag.FlagSet, url *string) string {
getCmd.Parse(os.Args[2:])
if *url == "" {
fmt.Print("url is required")
getCmd.PrintDefaults()
os.Exit(1)
}
urlResponse := GetUrl(*url)
return string(urlResponse)
}
func HandlePost(postCmd *flag.FlagSet, postUrl *string, postString string) string {
postCmd.Parse(os.Args[2:])
if *postUrl == "" || postString == "" {
fmt.Println("All fields required")
postCmd.PrintDefaults()
os.Exit(1)
}
post := Poster(*postUrl, postString)
return string(post)
}
In the "main.go" file we create two commands "get" and "post" with "url" as subcommands for both. We use a switch statement to handle both cases. The case "get" executes a function named "HandleGet" that receives the URL as an argument and prints the response.
In the case "post" we have to paste the JSON we want to post and it is taken as an argument by "HandlePost" and the response is printed.
In "HandlePost" we have to pass an URL and a JSON as arguments, if not, we will get an error. If an URL and a JSON are passed as arguments, "Poster" will be executed with the URL and the JSON as arguments.
Instructions
Open the terminal inside the program directory
Get case:
clihttprequest get -url <url>
Post case:
clihttprequest post -url <url>
The program will print "Enter Body: "
And you have to write or paste a JSON string like this:
{"id": "3", "Title": "Hello 3", "desc": "Article 3 description", "content": "Article 3 content"}
In this image, the JSON that has been posted has blank values because one of the keys is not a string, I wrote: content instead of "content".
I built this program to help me test my APIs. When I built it I knew little about programs that do the same thing but better.
Honestly, I spent weeks trying to build it, but it was fun.
Here is the source code
Follow me on Twitter