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"
}
Related
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.
Im using postman to post data and in the body Im putting some simple json
Request Body
{
"order":"1",
"Name":"ts1"
}
I need to transfer the data to json and I try like following,
and I wasnt able to get json, any idea what is missing
router.POST("/user", func(c *gin.Context) {
var f interface{}
//value, _ := c.Request.GetBody()
//fmt.Print(value)
err2 := c.ShouldBindJSON(&f)
if err2 == nil {
err = client.Set("id", f, 0).Err()
if err != nil {
panic(err)
}
}
The f is not a json and Im getting an error, any idea how to make it work?
The error is:
redis: can't marshal map[string]interface {} (implement encoding.BinaryMarshaler)
I use https://github.com/go-redis/redis#quickstart
If I remove the the body and use hard-coded code like this I was able to set the data, it works
json, err := json.Marshal(Orders{
order: "1",
Name: "tst",
})
client.Set("id", json, 0).Err()
If you only want to pass the request body JSON to Redis as a value, then you do not need to bind the JSON to a value. Read the raw JSON from the request body directly and just pass it through:
jsonData, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
// Handle error
}
err = client.Set("id", jsonData, 0).Err()
let's do it with an example. Assume your request body has a user email like this:
{ email: "test#test.com" }
and now you want to get this email on the back-end. first, define a struct like the following:
type EmailRequestBody struct {
Email string
}
Now you can easily bind the email value in your request body to the struct you defined: first, define a variable for your struct and then bind the value:
func ExampleFunction(c *gin.Context) {
var requestBody EmailRequestBody
if err := c.BindJSON(&requestBody); err != nil {
// DO SOMETHING WITH THE ERROR
}
fmt.Println(requestBody.Email)
}
you can easily access the email value and print it out or do whatever you need :
fmt.Println(requestBody.Email)
Or you can use GetRawData() function as:
jsonData, err := c.GetRawData()
if err != nil{
//Handle Error
}
err = client.Set("id", jsonData, 0).Err()
If you want to get json body like other frameworks like express(Nodejs), you can do the following
bodyAsByteArray, _ := ioutil.ReadAll(c.Request.Body)
jsonBody := string(bodyAsByteArray)
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.
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")
I'm using the following code to get a response from a server after a post request:
type ResponseFromPost struct {
N_expediente string
Enviar string
}
func main(){
......
res, err := client.Do(req)
if err != nil {
return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var re ResponseFromPost
err = json.Unmarshal(body, &re)
fmt.Println(re.Enviar);
}
With this I get:
error: &{%!e(string=array) %!e(*reflect.rtype=&{32 2509985895 0 8 8 25 0x608170
[0x7703c0 <nil>] 0x730b80 0x69acb0 0x6116c0 0x7732c0})}
The value sent by the server is:
[{"n_expediente":"9","enviar":"2"}]
How can I use the json variables?
The json is an array of those objects having the strings n_expediente and enviar on each instance. In the Go you'll need an array of your type;
re := []ResponseFromPost{}
err := json.Unmarshal([]byte(`[{"n_expediente":"9","enviar":"2"}]`), &re)
fmt.Println(re[0].Enviar);
Here's an example to show what your model needs to be https://play.golang.org/p/d64Sict4AG
You'll probably want to make some other changes to your code based on that. Like a loop bound by the length of the slice ( for i := range re { print i } ) after the unmarshal and a different name for the type.