I'm building a golang application which performs a POST to a Telegram Channel using a given Bot token but when I do it I get
400 Bad Request
This is my POST:
import (
"fmt"
"net/url"
"net/http"
"strings"
)
. . .
request_url := "https://api.telegram.org/bot{token}/sendMessage?chat_id={channelId}"
urlData := url.Values{}
urlData.Set("text", "Hello!")
client := &http.Client{}
req, _ := http.NewRequest("POST", request_url, strings.NewReader(urlData.Encode()))
req.Header.Set("content-type", "application-json")
res, err := client.Do(req)
if(err != nil){
fmt.Println(err)
} else {
fmt.Println(res.Status)
}
I don't get why it's giving me 400 even thought I'm able to perform the very same POST using Postman
POST https://api.telegram.org/bot{token}/sendMessage?chat_id={channelId}
body : {"text" : "Hello"} Content-Type=application/json
Any Hint on how to fix this?
I've been scratching my head for a while but I wasn't able to solve this.
UPDATE
Trying #old_mountain approach leads to the same result
import (
"fmt"
"net/http"
"bytes"
"encoding/json"
)
request_url := "https://api.telegram.org/bot{token}/sendMessage?chat_id={channelId}"
client := &http.Client{}
values := map[string]string{"text": "Hello!"}
jsonStr, _ := json.Marshal(values)
req, _ := http.NewRequest("POST", request_url, bytes.NewBuffer(jsonStr))
req.Header.Set("content-type", "application-json")
res, err := client.Do(req)
if(err != nil){
fmt.Println(err)
} else {
fmt.Println(res.Status)
}
You need to send a json string.
var jsonStr = []byte(`{"text":"Hello!"}`)
req, _ := http.NewRequest("POST", request_url, bytes.NewBuffer(jsonStr))
Or, if you don't want to directly write it:
values := map[string]string{"text": "Hello!"}
jsonStr, _ := json.Marshal(values)
req, _ := http.NewRequest("POST", request_url, bytes.NewBuffer(jsonStr))
Also, adjust the header Content-Type to:
req.Header.Set("Content-Type", "application/json")
Related
I want to get all new events from docker via the golang integration.
The problem is that it returns two channels and I couldn't figure out how to subscribe to them.
cli, err := client.NewClientWithOpts(client.WithVersion("1.37"))
if err != nil {
panic(err)
}
ctx, _ := context.WithCancel(context.Background())
msg, err := <- cli.Events(ctx, types.EventsOptions{})
There are many solutions. A solution could be:
msgs, errs := cli.Events(ctx, types.EventsOptions{})
for {
select {
case err := <-errs:print(err)
case msg := <-msgs:print(msg)
}
}
I am trying to send output from a docker container to the console using fmt, but when trying to do it i get this.
&{0xc0422a65c0 {0 0} false <nil> 0x6415a0 0x641540}
How do I do this? This is my full code.
func main() {
imageName := "hidden/hidden"
ctx := context.Background()
cli, err := client.NewClient("tcp://0.0.0.0:0000", "v0.00", nil, nil)
if err != nil {
panic(err)
}
fmt.Println("Pulling \"" + imageName + "\"")
_, err = cli.ImagePull(ctx, imageName, types.ImagePullOptions{})
if err != nil {
panic(err)
}
containerConfig := &container.Config{
Image: imageName,
Cmd: []string{"./app/start.sh", "--no-wizard"},
}
resp, err := cli.ContainerCreate(ctx, containerConfig, nil, nil, "")
if err != nil {
panic(err)
}
if err := cli.ContainerStart(ctx, resp.ID, types.ContainerStartOptions{}); err != nil {
panic(err)
}
timer := time.NewTimer(time.Minute)
go func() {
<-timer.C
if err := cli.ContainerStop(ctx, resp.ID, nil); err != nil {
panic(err)
}
}()
out, err := cli.ContainerLogs(ctx, resp.ID, types.ContainerLogsOptions{ShowStdout: true, ShowStderr: true, Follow: true})
if err != nil {
panic(err)
}
io.Copy(os.Stdout, out) // This is what I want to change to print with "fmt".
}
Tried: (but does not display until container is done.)
buf := new(bytes.Buffer)
buf.ReadFrom(out)
fmt.Println(buf.String())
Intention: Allow real-time console output to the web.
This seems to be the answer to my question, I did some searching about scanners as Cerise Limón commented. Anyone else who seems to be having the issue that I did can use this code. Thanks to all that helped.
scanner := bufio.NewScanner(out)
scanner.Split(bufio.ScanLines)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
&{0xc0422a65c0 {0 0} false <nil> 0x6415a0 0x641540} is not a bad output. This is a perfectly fine struct output. I think the the main problem is here just your lack of golang experience.
I'm a beginner as well and I can imagine that when you see an output like above you thought that "I made a mistake"
No you didn't. It's default fmt behavior when u try to print out a struct which contains pointers.
Checkout this instead of your fmt.Println:
fmt.Printf("%+v\n", out)
Well this answer stands on my assumptions but if this is the case just ping me for more info.
I need to send http request to https://some-domain.com/getsomething/?id=myID
I have url and need to add to it a query parameter. Here is my Go code
baseUrl := "https://some-domain.com"
relativeUrl := "/getsomething/"
url, _ := url.Parse(baseUrl)
url.Path = path.Join(url.Path, relativeUrl)
// add parameter to query string
queryString := url.Query()
queryString.Set("id", "1")
// add query to url
url.RawQuery = queryString.Encode()
// print it
fmt.Println(url.String())
In output I see this url: https://some-domain.com/getsomething?id=1
And this one is required: https://some-domain.com/getsomething/?id=1
You can see that there is no / character before ?.
Do you know how to fix it without manual string manipulations?
https://play.golang.org/p/HsiTzHcvlQ
You can use ResolveReference.
package main
import (
"fmt"
"log"
"net/url"
)
func main() {
relativeUrl := "/getsomething/"
u, err := url.Parse(relativeUrl)
if err != nil {
log.Fatal(err)
}
queryString := u.Query()
queryString.Set("id", "1")
u.RawQuery = queryString.Encode()
baseUrl := "https://some-domain.com"
base, err := url.Parse(baseUrl)
if err != nil {
log.Fatal(err)
}
fmt.Println(base.ResolveReference(u))
}
https://play.golang.org/p/BIU29R_XBM
Is there a way in Go to combine URL paths similarly as we can do with filepaths using path.Join()?
For example see e.g. Combine absolute path and relative path to get a new absolute path.
When I use path.Join("http://foo", "bar"), I get http:/foo/bar.
See in Golang Playground.
The function path.Join expects a path, not a URL. Parse the URL to get a path and join with that path:
u, err := url.Parse("http://foo")
if err != nil { log.Fatal(err) }
u.Path = path.Join(u.Path, "bar.html")
s := u.String()
fmt.Println(s) // prints http://foo/bar.html
Use the url.JoinPath function in Go 1.19 or later:
s, err := url.JoinPath("http://foo", "bar.html")
if err != nil { log.Fatal(err) }
fmt.Println(s) // prints http://foo/bar.html
Use ResolveReference if you are resolving a URI reference from a base URL. This operation is different from a simple path join: an absolute path in the reference replaces the entire base path; the base path is trimmed back to the last slash before the join operation.
base, err := url.Parse("http://foo/quux.html")
if err != nil {
log.Fatal(err)
}
ref, err := url.Parse("bar.html")
if err != nil {
log.Fatal(err)
}
u := base.ResolveReference(ref)
fmt.Println(u.String()) // prints http://foo/bar.html
Notice how quux.html in the base URL does not appear in the resolved URL.
ResolveReference() in net/url package
The accepted answer will not work for relative url paths containing file endings like .html or .img. The ResolveReference() function is the correct way to join url paths in go.
package main
import (
"fmt"
"log"
"net/url"
)
func main() {
u, err := url.Parse("../../..//search?q=dotnet")
if err != nil {
log.Fatal(err)
}
base, err := url.Parse("http://example.com/directory/")
if err != nil {
log.Fatal(err)
}
fmt.Println(base.ResolveReference(u))
}
A simple approach to this would be to trim the /'s you don't want and join. Here is an example func
func JoinURL(base string, paths ...string) string {
p := path.Join(paths...)
return fmt.Sprintf("%s/%s", strings.TrimRight(base, "/"), strings.TrimLeft(p, "/"))
}
Usage would be
b := "http://my.domain.com/api/"
u := JoinURL(b, "/foo", "bar/", "baz")
fmt.Println(u)
This removes the need for checking/returning errors
In 1.19 there will be a new function in the standard library that solves this very neatly.
u, err := url.JoinPath("http://host/foo", "bar/")
https://go.dev/play/p/g422ockBq0q?v=gotip
To join a URL with another URL or a path, there is URL.Parse():
func (u *URL) Parse(ref string) (*URL, error)
Parse parses a URL in the context of the receiver. The provided URL
may be relative or absolute. Parse returns nil, err on parse failure,
otherwise its return value is the same as ResolveReference.
func TestURLParse(t *testing.T) {
baseURL, _ := url.Parse("http://foo/a/b/c")
url1, _ := baseURL.Parse("d/e")
require.Equal(t, "http://foo/a/b/d/e", url1.String())
url2, _ := baseURL.Parse("../d/e")
require.Equal(t, "http://foo/a/d/e", url2.String())
url3, _ := baseURL.Parse("/d/e")
require.Equal(t, "http://foo/d/e", url3.String())
}
I wrote this utility function that works for my purposes:
func Join(basePath string, paths ...string) (*url.URL, error) {
u, err := url.Parse(basePath)
if err != nil {
return nil, fmt.Errorf("invalid url")
}
p2 := append([]string{u.Path}, paths...)
result := path.Join(p2...)
u.Path = result
return u, nil
}
https://play.golang.org/p/-QNVvyzacMM
How do we read from a url resource. I have used the https://github.com/Kissaki/rest.go api in the following example. Below is the example I use to write a string to the url http://localhost:8080/cool
But now I need to retrieve the data from the url, how do I read it back?
package main
import (
"fmt"
"http"
"github.com/nathankerr/rest.go"
)
type FileString struct {
value string
}
func (t *FileString) Index(w http.ResponseWriter) {
fmt.Fprintf(w, "%v", t.value)
}
func main(){
rest.Resource("cool", &FileString{s:"this is file"})
http.ListenAndServe(":3000", nil)
}
if you just want to fetch a file over http you could do something like this i guess
resp, err := http.Get("http://your.url.com/whatever.html")
check(err) // does some error handling
// read from resp.Body which is a ReadCloser
response, err := http.Get(URL) //use package "net/http"
if err != nil {
fmt.Println(err)
return
}
defer response.Body.Close()
// Copy data from the response to standard output
n, err1 := io.Copy(os.Stdout, response.Body) //use package "io" and "os"
if err != nil {
fmt.Println(err1)
return
}
fmt.Println("Number of bytes copied to STDOUT:", n)