I have a HTTP POST request with payload
indices=0%2C1%2C2
Here is my golang backend code
err := r.ParseForm()
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("r.PostForm", r.PostForm)
log.Println("r.Form", r.Form)
body, err := ioutil.ReadAll(r.Body)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("r.Body", string(body))
values, err := url.ParseQuery(string(body))
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
log.Println("indices from body", values.Get("indices"))
Output:
r.PostForm map[]
r.Form map[]
r.Body indices=0%2C1%2C2
indices from body 0,1,2
Why is it that the POST request is not parsed by r.ParseForm(), while manaully parsing it with url.ParseQuery(string(body)) gives the correct result?
The problem is not in your server code which is fine, but simply that your client, whatever it is, is missing the correct Content-Type header for POST forms. Simply set the header to
Content-Type: application/x-www-form-urlencoded
In your client.
Get value form your params use PostFormValue("params") from your http.Request
err := r.ParseForm()
if err != nil{
panic(err)
}
params := r.PostFormValue("params") // to get params value with key
Related
I need to access a private docker registry using the Go SDK. I found "package registry".
I see it has "DefaultSession" object. I can connect to the private registry, but I can't investigate it using the DefaultSession.
Secondly, the registry package contains the Session struct. They wrote it's for the v1 protocol only. Ok, I connect the private repository using the Session:
c := http.Client{}
indexInfo, err := registry.ParseSearchIndexInfo("repo")
if err != nil {
log.Error(err)
return
}
endpoint, err := registry.NewV1Endpoint(indexInfo, "", nil)
if err != nil {
log.Error(err)
return
}
session, err := registry.NewSession(&c, &authConfig, endpoint)
if err != nil {
log.Error(err)
return
}
n, err := reference.ParseNamed("docker.io/repo/image")
if err != nil {
log.Error(err)
return
}
rep, err := session.GetRepositoryData(n)
if err != nil {
log.Error(err)
return
}
But GetRepositoryData returns zero images, but they are in the repository. Why?
Is it right way I do access to a remote repository? Is there a v2 SDK for Go?
I have a need to programmatically (using golang) login to gcr.io docker registry using this package library https://godoc.org/github.com/docker/docker/client
I have tried using it, i can successfully login but upon pushing an image to my gcr.io project registry, it said
{"errorDetail":{"message":"unauthorized: You don't have the needed permissions to perform this operation, and you may have invalid credentials. To authenticate your request, follow the steps in: https://cloud.google.com/container-registry/docs/advanced-authentication"},"error":"unauthorized: You don't have the needed permissions to perform this operation, and you may have invalid credentials. To authenticate your request, follow the steps in: https://cloud.google.com/container-registry/docs/advanced-authentication"}
My code looks like this
package client
import (
"context"
"fmt"
"io"
"os"
"github.com/docker/docker/api/types"
dockerClient "github.com/docker/docker/client"
)
type Service struct{
DockerClient *dockerClient.Client
}
type CopyImageOptions struct {
DestRegistryAuth string
}
type DockerImageService interface {
CopyImage(ctx context.Context, source, dest string, option CopyImageOptions)
}
// NewDockerClient returns a client
func NewDockerClient() *Service {
cli, err := dockerClient.NewEnvClient()
if err != nil {
panic(err)
}
return &Service{DockerClient: cli}
}
func (s *Service) CopyImage(ctx context.Context, source, dest string, option CopyImageOptions) error {
rc, err := s.DockerClient.ImagePull(ctx, source, types.ImagePullOptions{})
if err != nil{
return fmt.Errorf("error when pulling source image. err: %v", err)
}
defer rc.Close()
io.Copy(os.Stdout, rc)
destClient := NewDockerClient()
if option.DestRegistryAuth != "" {
//current use case we can assume that the dest is on asia.gcr.io
status, err := destClient.DockerClient.RegistryLogin(ctx, types.AuthConfig{
Username: "oauth2accesstoken",
Password: option.DestRegistryAuth,
ServerAddress: "asia.gcr.io",
})
if err != nil{
return fmt.Errorf("error when login to destination image registry. err: %v", err)
}
fmt.Println(status)
}
err = destClient.DockerClient.ImageTag(ctx, source, dest)
if err != nil {
return fmt.Errorf("error when tagging image. err: %v", err)
}
rc, err = destClient.DockerClient.ImagePush(ctx, dest, types.ImagePushOptions{
RegistryAuth: option.DestRegistryAuth,
})
if err != nil{
return fmt.Errorf("error when pushing image to destionation. err: %v", err)
}
defer rc.Close()
io.Copy(os.Stdout, rc)
return nil
}
You may take a look at the CopyImage method, where the option.DestRegistryAuth is assigned with the output gcloud auth print-access-token. The username is set to "oauth2accesstoken" because I followed this instruction: https://cloud.google.com/container-registry/docs/advanced-authentication
As for the source parameter, it is assumed it's from public registry like docker.io/library/alpine:3.10, so we can pull it without having configuring any auth token. However for the dest parameter, currently it is an image in my private registry such as: asia.gcr.io/<gcp-project-id>/alpine:3.10
Also, the gcloud auth print-access-token is called after I did gcloud auth loginand I already had full permission to access my private asia.gcr.io registry (assigned on bucket level).
Now the weird thing is I can successfully push it using docker push command, right after do docker login described in here https://cloud.google.com/container-registry/docs/advanced-authentication .
Any advice?
Okay I just found out what the mistake is on my code above. I realized this after looking at example code on pulling image from private registry here: https://docs.docker.com/develop/sdk/examples/#pull-an-image-with-authentication
As it turns out, the RegistryAuth arg in types.ImagePush options expect a base64 encoding string.
So with this code, I can successfully push local image to my private registry.
authConfig := types.AuthConfig{
Username: "oauth2accesstoken",
Password: option.DestRegistryAuth,
}
encodedJSON, err := json.Marshal(authConfig)
if err != nil {
return fmt.Errorf("error when encoding authConfig. err: %v", err)
}
authStr := base64.URLEncoding.EncodeToString(encodedJSON)
rc, err = destClient.DockerClient.ImagePush(ctx, dest, types.ImagePushOptions{
RegistryAuth: authStr,
})
I can tweet, and upload media, but I cannot figure out how to tweet with the media using anaconda("github.com/ChimeraCoder/anaconda"). The media_id in the example was from a sucessfull media upload call.
mediaResponse, err := api.UploadMedia("R0lGODlhEAALALMMAOXp8a2503CHtOrt9L3G2+Dl7vL0+J6sy4yew1Jvp/T2+e/y9v///wAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh+QQFCwAMACwAAAAAEAALAAAEK5DJSau91KxlpObepinKIi2kyaAlq7pnCq9p3NZ0aW/47H4dBjAEwhiPlAgAIfkECQsADAAsAAAAAAQACwAABA9QpCQRmhbflPnu4HdJVAQAIfkECQsADAAsAAAAABAACwAABDKQySlSEnOGc4JMCJJk0kEQxxeOpImqIsm4KQPG7VnfbEbDvcnPtpINebJNByiTVS6yCAAh+QQJCwAMACwAAAAAEAALAAAEPpDJSaVISVQWzglSgiAJUBSAdBDEEY5JMQyFyrqMSMq03b67WY2x+uVgvGERp4sJfUyYCQUFJjadj3WzuWQiACH5BAkLAAwALAAAAAAQAAsAAAQ9kMlJq73hnGDWMhJQFIB0EMSxKMoiFcNQmKjKugws0+navrEZ49S7AXfDmg+nExIPnU9oVEqmLpXMBouNAAAh+QQFCwAMACwAAAAAEAALAAAEM5DJSau91KxlpOYSUBTAoiiLZKJSMQzFmjJy+8bnXDMuvO89HIuWs8E+HQYyNAJgntBKBAAh+QQFFAAMACwMAAIABAAHAAAEDNCsJZWaFt+V+ZVUBAA7")
if err != nil {
fmt.Println(err)
}
//v := url.Values{}
//v.Set("media_ids", string(mediaResponse.MediaID))
fmt.Println(mediaResponse)
tweet := `
"media_ids": 612877656984416256,
"status": "hello"
`
result, err := api.PostTweet(tweet, nil)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(result)
}
Can someone assist in telling me how to parse the json or call the PostTweet with the media id? I've also tried adding the media to url.Values without sucess.
Thanks everyone. I see that the json was invalid but the issue was an error passing the media_ids parameter. The response was: "errors":[{"code":44,"message":"media_ids parameter is invalid."}] which i though erroring out on the formatting but it had to do with not converting the media_ids type int64 to a string correctly. Here is the fixed code:
data, err := ioutil.ReadFile(fileName)
if err != nil {
fmt.Println(err)
}
mediaResponse, err := api.UploadMedia(base64.StdEncoding.EncodeToString(data))
if err != nil {
fmt.Println(err)
}
v := url.Values{}
v.Set("media_ids", strconv.FormatInt(mediaResponse.MediaID, 10))
result, err := api.PostTweet(posttitle, v)
if err != nil {
fmt.Println(err)
} else {
fmt.Println(result)
}
This is not valid json:
tweet := `
"media_ids": 612877656984416256,
"status": "hello"
`
Try using this to generate your json:
type Tweet struct {
MediaIds uint64 `json:"media_ids"`
Status string `json:"status"`
}
tweet := Tweet{612877656984416256, "hello"}
b, err := json.Marshal(tweet)
This results in :
{"media_ids":612877656984416256,"status":"hello"}
This has a few benefits over using a raw string.
It is more go centric. The struct can be passed around with values set and read with proper type checking caught at compile time.
The generated json string is more likely to be semantically correct. e.g. Go will also escape certain characters to help ensure they will be parsed properly by the receiver.
I'm using Alamofire to get data from my server. However, it doesn't catch the error, as the error returned is nil. I've tested with AFNetworking, and it works fine. For both operation, the status code returned is 401 Unauthorized . Is there's something with my code?
I'm using GrapeAPI for my backend. All it does is just to return the error on fail request
GrapeAPI
error!('Unauthorized', 401)
AFNetworking
manager.GET("someUrl", parameters: nil, success: { (_, object) in
}, failure: { (operation, error) in
// These are the outputs. I'm not assigning any values
// error.localizedDescription = "Request failed: unauthorized (401)"
// statusCode = 401
})
Alamofire
Alamofire.request(.GET, "url", parameters: nil)
.response { (a,b,data,error) in
// These are the outputs. I'm not assigning any values
// error = nil
// data = {"error":"Unauthorized"}
// statusCode = 401
}
I can check the failure using the statusCode. But I prefer to check the error object instead. However, since the error is nil in Alamofire, it's quite confusing to check whether the request has failed or not.
As Matt has mentioned in the comment, I need to add .validate() before calling .response(). This is by design. Final code as below:
Alamofire.request(.GET, "url", parameters: nil)
.validate()
.response { (a,b,data,error) in
// error won't be nil now
// and statusCode will be 401
}
Read this detailed explanation(thanks!) for more information.
Alamofire does not see 401 Unauthorized as an error as it is an valid return. In your comment code you are assigning a value to error not checking it for error, it should be:
Alamofire.request(.GET, "url", parameters: nil)
.response { (a,b,data,error) in
if error != nil{
println(error.localizedDescription)
} else {
if let data = data{
//You should probably use a switch statement here
if data.statusCode == 401 {
println("Unauthorized")
} else if data.statusCode == 200 {
println("Success")
}
}
}
I am not sure if i understand correctly your problem, but I hope that help!
This is related to this other question. I'm fetching a URL through a proxy using this simple code:
package main
import (
"fmt"
"io/ioutil"
"net/http"
"net/url"
)
func main() {
proxyUrl, err := url.Parse("87.236.233.92:8080")
httpClient := &http.Client { Transport: &http.Transport { Proxy: http.ProxyURL(proxyUrl) } }
response, err := httpClient.Get("http://stackoverflow.com")
if err != nil {
fmt.Println(err.Error())
} else {
body, _ := ioutil.ReadAll(response.Body)
fmt.Println("OK: ", len(body))
}
}
If I run this code, I am getting this error:
Get http://stackoverflow.com: http: error connecting to proxy 87.236.233.92:8080: GetServByName: The requested name is valid, but no data of the requested type was found.
I know that the proxy address is valid and if I fetch the URL through the proxy by other means it work. Any idea why I'm getting this error?
Specify your proxy with http:// in and it should work, eg
proxyUrl, err := url.Parse("http://87.236.233.92:8080")
if err != nil {
fmt.Println("Bad proxy URL", err)
return
}