I'm sending POST request:
req, err := http.NewRequest("POST", link, bytes.NewBuffer(jsonStr))
client := &http.Client{Timeout: tm}
resp, err := client.Do(req)
I receive resp.Header in format with type http.Header
I need to something like this:
[
"Server: nginx/1.4.4",
"Date: Wed, 24 Feb 2016 19:09:49 GMT"
]
I don't know how to approach this problem, because I don't know how to deal with http.Header datatype. Could someone help please
resp.Header is of type http.Header. You can see in the documentation that this type is also a map, so you can access it in two different ways:
1) By using http.Header's methods:
serverValue := resp.Header().Get("Server")
dataValue := resp.Header().Get("Date")
If the header exists, you'll get its first value (keep in mind that there might be multiple values for a single header name); otherwise you'll get an empty string.
2) By using map's methods:
serverValue, ok := resp.Header()["Server"]
dataValue, ok := resp.Header()["Date"]
If the header exists, ok will be true (i.e. the header exists) and you'll get a slice of strings containing all the values for that header; otherwise, ok will be false (i.e. the header doesn't exist).
Use whichever method you prefer.
If you need to iterate over all the header values, you can do it with something like:
for name, value := range resp.Header() {
fmt.Printf("%v: %v\n", name, value)
}
You could use a function like this one:
func HeaderToArray(header http.Header) (res []string) {
for name, values := range header {
for _, value := range values {
res = append(res, fmt.Sprintf("%s: %s", name, value))
}
}
return
}
It should return an array like the one you want.
This solutions is for go version go1.13 windows/amd64.
The request object from http.Request contains Header object. We are using net/http package here. You can get values of all headers by name using following method:
import(
"net/http"
)
type RequestHeaders struct {
ContentType string `json: "content-type"`
Authorization string `json: "authorization"`
}
func getHeaders(r *http.Request) RequestHeaders {
contentType := r.Header.Get("Content-Type")
authorization := r.Header.Get("Authorization")
headers := RequestHeaders{
Content-Type: contentType,
Authorization: authorization}
return headers
}
You can see that we are using r.Header.Get("Content-Type") method to get value of the header.
If the header is missing the Get() method will return empty string.
You can retrieve the response header's first value with resp.Header().Get(), which returns "" if the header key has no values.
Hence, in your case
var a [2]string
a[0] = resp.Header().Get("server")
a[1] = resp.Header().Get("date")
Related
I'm using range to loop through an array of structs to extract data which will be used as a URL parameter for my API calls. Within this loop, I'm trying to push response data from one struct to another.
I'm able to get everything working, except for moving data from one struct to another, but not entirely sure how to solve for the errors I keep getting. I've tried multiple methods and seem to be stuck in the mud here for something I don't consider to be too hard, until now... In my code I'm using the append method but I'm not so sure that might be the correct way to proceed.
Presenting my code:
models.go
//Here is my existing struct, with populated data that I get from a CSV
type TravelItenaries struct {
Origin string
Destination string
Flight_num string
Origin_latitude string
Origin_longitude string
Destination_latitude string
Destination_longitude string
Origin_weather string
Destination_weather string
Coordinates_ori string
Coordinates_dest string
Temp_c_ori string
Temp_f_ori string
Temp_c_dest string
Temp_f_dest string
}
//Here is the response data that I'm expected to get from my API calls.
//I'm trying to "push" Temp_c_dest and Temp_f_dest data into TravelItenaries.Temp_f_dest and TravelItenaries.Temp_c_dest
//While also changing the data types to fit above.
type Response struct {
Current struct {
LastUpdatedEpoch int `json:"last_updated_epoch"`
LastUpdated string `json:"last_updated"`
Temp_c_dest float64 `json:"temp_c"`
Temp_c_dest float64 `json:"temp_f"`
IsDay int `json:"is_day"`
} `json:"current"
}
weather.go
func (s *Server) getWeather(w http.ResponseWriter, r *http.Request) {
// open file
f, err := os.Open("challenge_dataset.csv")
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
// remember to close the file at the end of the program
defer f.Close()
// read csv values using csv.Reader
csvReader := csv.NewReader(f)
data, err := csvReader.ReadAll()
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
// convert records to array of structs
travelItenaries := createTravelItenaries(data)
// remove duplicate flight records
cleanTravelItenaries:= remDupKeys(travelItenaries)
// set up params for API get request
params := url.Values{
"key": []string{"xxx"},
"q": []string{""},
}
// Construct URL for API request
u := &url.URL{
Scheme: "https",
Host: "api.weatherapi.com",
Path: "/v1/current.json",
RawQuery: params.Encode(),
}
client := &http.Client{}
// Will need this to populate the params using a range over a struct
values := u.Query()
// loop through cleaned data set
for _, service := range cleanTravelItenaries {
// dynamically acquire data from struct to pass as parameter
values.Set("q", service.Coordinates_dest)
u.RawQuery = values.Encode()
req, err := http.NewRequest("GET", u.String(), nil)
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
resp, err := client.Do(req)
if err != nil {
responses.ERROR(w, http.StatusBadRequest, fmt.Errorf("helpful error"))
return
}
body, err := ioutil.ReadAll(resp.Body)
// create empty struct to parse response data with using Inmarshal
var responseData models.Response
json.Unmarshal(body, &responseData)
// Here is the issue, I don't think append might be the correct procedure here?
// I simply just need to pass this response data to my already existing struct
service.Temp_c_dest = append(responseData.Current.Temp_c_dest , cleanTravelItenaries )
service.Temp_f_dest = append(responseData.Current.Temp_f_dest , cleanTravelItenaries )
}
}
The errors I get are related to both append statements at the end of the range function.
first argument to append must be slice; have float64
first argument to append must be slice; have float64
for both append methods.
Also, note how type TravelItenaries struct uses string type for:
Temp_c_dest string
Temp_f_dest string
Hence why I also need to do some field type conversion from Float64 to string.
How can I extract the fields Temp_c_dest and Temp_f_dest from API response struct to TravelItenaries struct fields while changing datatypes?
EDIT:
I've managed to get this somewhat working, but only inside the for loop. The data is not being saved outside the function.
service.Temp_f_dest = strconv.FormatFloat(responseData.Current.Temp_f_dest, 'g', -1, 64)
service.Temp_c_dest = strconv.FormatFloat(responseData.Current.Temp_c_dest, 'g', -1, 64)
According to Coinbase pro API docs:
The CB-ACCESS-SIGN header is generated by creating a sha256 HMAC using the base64-decoded secret key on the prehash string timestamp +
method + requestPath + body (where + represents string concatenation)
and base64-encode the output. The timestamp value is the same as the
CB-ACCESS-TIMESTAMP header.
The body is the request body string or omitted if there is no request
body (typically for GET requests).
The method should be UPPER CASE.
I borrowed a signing function from a better programmer and feed it something like this:
1619383731POST/reports{{"end_date":"2021-01-02T11:59:59Z","start_date":"2020-01-01T00:00:00Z","type":"account"}}
But keep getting invalid signature from Coinbase.
Signing function for reference:
// sign
func (e *exchange) sign(msg string) string {
key, err := base64.StdEncoding.DecodeString(e.http.secret)
if e.checkErr(err) {
return "bad_sig"
}
signature := hmac.New(sha256.New, key)
_, err = signature.Write([]byte(msg))
if e.checkErr(err) {
return "bad_sig"
}
return base64.StdEncoding.EncodeToString(signature.Sum(nil))
}
Where am I screwing up?
Have you checked out the go-coinbase github repo implementing this as:
https://github.com/preichenberger/go-coinbasepro/blob/master/client.go
h := make(map[string]string)
h["CB-ACCESS-KEY"] = c.Key
h["CB-ACCESS-PASSPHRASE"] = c.Passphrase
h["CB-ACCESS-TIMESTAMP"] = timestamp
message := fmt.Sprintf(
"%s%s%s%s",
timestamp,
method,
url,
data,
)
sig, err := generateSig(message, c.Secret)
if err != nil {
return nil, err
}
h["CB-ACCESS-SIGN"] = sig
return h, nil
It turns out using req.Body directly is the culprit. Why I don't know and something I need to find out, but reading into a []byte and then casting to a string solves part of it.
I have a use case where I have the code as below. I have a request coming in to hit the backend where I need to append data to a map. My question is how do I convert the below type to a []byte to unmarshal?
Any ideas would be appreciated.
type Example struct {
Category string `json:"category"`
Name string `json:"name"`
}
Incoming Postman request json looks like this:
[{"Category":"TestCategory", "Name":"Sample1"}]
but after doing
jsonString Type: []Example
if err := gc.ShouldBindJSON(&jsonString) it looks like [{TestCategory Sample1}] ; how do I convert this to a []byte?
for _, req := range blob{
var jsonString Example
if err := json.Unmarshal([]byte(jsonString), &blob); err != nil { //this does not work
logger.Fatal(err)
}
//I am checking if a key-value is present and appending it to the map
dict := make(map[string][]Example)
dict[req.Category] = append(dict[req.Category], req)
fmt.Println(dict)
if value, ok := dict["TestCategory"]; ok {
fmt.Printf("Found %d\n", value)
} else {
fmt.Println("not found")
}
}
//I was able to test the above logic by declaring the jsonString as a const and it works
There are two directions in which you can move the data:
from JSON to a Go data structure
// This is your payload coming from the request.
jsonStr := `[{"Category":"TestCategory", "Name":"Sample1"}]`
// This is the Go struct that will hold the unmarshalled data.
var examples []Example
err := json.Unmarshal([]byte(jsonStr), &examples)
if err != nil {
log.Fatal(err)
}
fmt.Println("Examples:", examples) // prints "Examples: [{TestCategory Sample1}]"
from a Go data structure to JSON (either string or []byte)
exampleBytes, err := json.Marshal(examples)
if err != nil {
log.Fatal(err)
}
fmt.Println("Example bytes:", string(exampleBytes)) // prints "Example bytes: [{"category":"TestCategory","name":"Sample1"}]"
You should check out "Go by Example" if you haven't already: https://gobyexample.com/json
Looking at your code:
You are looping on blob but instead of using the req you are trying to unmarshal onto the entire blob each time. I'm not sure what you are trying to achieve there but nothing good can come out of changing a struct you're looping over from within the loop.
The request JSON you are listing is an array of JSON objects. You are trying to unmarshal that into a single Example struct. That won't work, you need an array of those.
I'm looking for an efficient way to read HTTP headers from a textfile to be later sent with an HTTP request. Consider the following code (which currently contains basic net/http request functionality):
func MakeRequest(target string, method string) {
client := &http.Client{}
req, _ := http.NewRequest(method, target, nil)
//Headers manually..
req.Header.Add("If-None-Match", `some value`)
response, _ := client.Do(req)
body, _ := ioutil.ReadAll(response.Body)
fmt.Println(string(body))
}
I started by using ioutil.ReadFile like this:
func main() {
data, _ := ioutil.ReadFile("/opt/tests/req.txt")
fmt.Print(string(data))
}
But taking this text, splitting it by some indicator (lets say ":") and then placing the information in req.Header.Add("var1", "var2") per-header seems like an over-kill.
Question: Any better way to send HTTP requests with headers from a text file in go?
net/http has a method ReadRequest which can create a new Request object from a bufio.Reader. Assuming that your file contains a real HTTP request (instead of only the part of the request which consists of lines with key: value) all you need to do is create a new bufio.Reader from the file, i.e. like this (error handling omitted):
rdr,_ := os.Open("req.txt")
req,_ := http.ReadRequest(bufio.NewReader(rdr))
fmt.Printf("%+v\n", req)
If you only want some headers defined, another option is to define the headers in a Json file and apply the following code (file reading not included):
var jsonMap map[string]string
err = json.Unmarshal(jsonBytesFromFile, &jsonMap)
if err != nil {
log.Fatal("unable to parse json: ", err)
}
for k, v := range jsonMap {
log.Printf("setting Header : %s : %s", k, v)
responseWriter.Header().Add(k, v) // you may prefer Set()
}
The json looks like this:
{
"Content-type": "text/plain",
"Cache-Control": "only-if-cached"
}
In curl, I can send post data by -d flag like below example
curl -X POST -d'{"accountID":"1"}' localhost:1234/geInfo
How am I supposed to send accountID value in go-wrk command for a post request?
Unless I am mistaken, it is (currently) not supported to pass post parameters.
I figured that from the code of go-wrk by following the -m="POST" parameter which suggests otherwise. (Offering the method "POST" of course does not imply you can also pass parameters)
The parameter is parsed in main.go:19:
method = flag.String("m", "GET", "the http request method")
then passed on to client in single_node.go:16:
go StartClient(
toCall,
*headers,
*method,
*disableKeepAlives,
responseChannel,
wg,
*totalCalls,
)
where it is received in third place in "meth" variable (client.go:14):
func StartClient(url_, heads, meth string, dka bool, responseChan chan *Response, waitGroup *sync.WaitGroup, tc int) {
and then used here (client.go:55):
req, _ := http.NewRequest(meth, url_, nil)
sets := strings.Split(heads, "\n")
//Split incoming header string by \n and build header pairs
for i := range sets {
split := strings.SplitN(sets[i], ":", 2)
if len(split) == 2 {
req.Header.Set(split[0], split[1])
}
}
timer := NewTimer()
for {
timer.Reset()
resp, err := tr.RoundTrip(req)
respObj := &Response{}
(...)
responseChan <- respObj
}
If post parameters would be passable, they would have to be put somewhere into the Request as you can lookup on the golang http package website:
func NewRequest(method, urlStr string, body io.Reader) (*Request, error)
NewRequest returns a new Request given a method, URL, and optional body.