I am new to GraphQL so I looked at my Postman request.
This is the raw Postman request.
Request Headers
Content-Type: application/json
User-Agent: PostmanRuntime/7.29.2
Accept: */*
Cache-Control: no-cache
Postman-Token: e3239924-6e8a-48b9-bc03-3216ea6da544
Host: localhost:4000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 94
Request Body
query: "query {
getTodo(todoId:3) {
id
text
done
}
}"
I tried the following
var url = "http://localhost:4000/query"
func TestGetTodoByID(t *testing.T) {
jsonData := `query {
getTodo(todoId:3) {
id
text
done
}
}`
fmt.Println(jsonData)
request, err := http.NewRequest("POST", url, bytes.NewBuffer([]byte(jsonData)))
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Content-Length", fmt.Sprint(len(jsonData)))
client := &http.Client{Timeout: time.Second * 10}
response, err := client.Do(request)
defer response.Body.Close()
if err != nil {
fmt.Printf("The HTTP request failed with error %s\n", err)
}
data, err := io.ReadAll(response.Body)
if err != nil {
fmt.Printf("Error reading response with error %s\n", err)
}
responseBody := string(data)
if status := response.StatusCode; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
expected := `{
"data": {
"getTodo": {
"id": 3,
"text": "qwert333 hello",
"done": true
}
}
}`
if responseBody != expected {
t.Errorf("handler returned unexpected body: got %v want %v",
responseBody, expected)
}
}
Then I got the following error:
api_test.go:41: handler returned wrong status code: got 400 want 200
api_test.go:54: handler returned unexpected body: got {"errors":[{"message":"json request body could not be decoded: invalid character 'q' looking for beginning of value body:query {\n getTodo(todoId:3) {\n id\n text\n done\n }\n}"}],"data":null} want {
Then I also tried exactly what I found in the Postman console
jsonData := `"query":"query {
getTodo(todoId:3) {
id
text
done
}
}"`
This gave me a slightly different error
api_test.go:41: handler returned wrong status code: got 400 want 200
api_test.go:54: handler returned unexpected body: got {"errors":[{"message":"json request body could not be decoded: json: cannot unmarshal string into Go value of type graphql.RawParams body:\"query\":\"query {\n getTodo(todoId:3) {\n id\n text\n done\n }\n}\""}],"data":null} want {
Any ideas?
The standard GraphQL JSON format is actually a JSON object. In a Go context, the easiest way to create this is with a structure annotated for the encoding/json package. (You may go looking for a dedicated GraphQL client package that has this structure predefined.)
So for example you might create a structure for the minimal form of that structure
type GraphQLRequest struct{
Query string `json:"query"`
Variables map[string]interface{} `json:"variables,omitempty"`
}
and then create an instance of that structure and serialize it
query := `query GetTodo($id: ID!) {
getTodo(todoId: $id) { id, name, done }
}`
request := GraphQLRequest{
Query: query,
Variables: map[string]interface{}{
"id": 3,
}
}
jsonData, err := json.Marshal(request)
if (err != nil) {
panic(err) // or handle it however you would otherwise
}
request, err := http.NewRequest("POST", url, jsonData)
...
You may find it similarly useful to json.Unmarshal() the response data into a structure that has the standard format, with a Data field matching the shape of your query.
Related
I am trying to send a request to a graphql endpoint. My problem is that
var jsonStr = []byte(`{ "query": "query { viewer { login } organization ( login:"org") { description } }" `)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
Returns "Problem parsing JSON" and
var jsonStr = []byte(`{ "query": "query { viewer { login } }" `)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
Gives me back the desired response. I am assuming that this has to do with something like whitespace or double quotes, but I am unsure exactly how to fix this issue. The only reason I am encasing a GraphQL query inside JSON is because bytes.NewBuffer will throw an error if given anything other than a JSON string and I cannot make the httpRequest without first passing the string into bytes.NewBuffer. Thank you so much for whatever insight you can give me.
The json in your jsonStr variable is invalid - the quotes within the "query" object need to be escaped.
You can manually escape them:
var jsonStr = []byte(`{ "query": "query { viewer { login } organization ( login:\"org\") { description } }" `)
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
Or, if I was writing this, I'd probably use JSON marshaling to construct this object to avoid needing to manually escape the JSON:
type QueryRequestBody struct {
Query string `json:"query"`
}
func main() {
var query = QueryRequestBody{
Query: `query { viewer { login } organization ( login:"org") { description } }`,
}
jsonStr, err := json.Marshal(query)
if err != nil {
panic(err)
}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonStr))
}
I am using this Go API client on my app https://github.com/heroku/docker-registry-client to interact with a docker registry using Go. The case is that internally that is having some issue when does a PUT request using the package "net/http".
When I run the following code I am getting this as error Put url: http: ContentLength=2821 with Body length 0. So it seems that net/http Client.Do() function is not getting the body I set at some point of the function. But as you can se right on the code below at some point I still have the JSON content that I want to send in a []byte.
body, err := manifest.MarshalJSON()
if err != nil {
return err
}
log.Println(string(body)) // I get the JSON data back here
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", manifestV2.MediaTypeManifest)
resp, err := registry.Client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
return err
As far as I have digged into it, the error comes from the net/http Client.do() function (golang.org/src/net/http/client.go line 514), and I'd say the error is triggered from Request.GetBody() function (from line 591 on Client).
So still trying to go deeper and do some tests to find out what is going on here.
Any clue?
In case the error is given by the server I'll have to get something like this, but in the response body and no errors on the net/http Client.Do() call.
Content-Length: <length>
Content-Type: application/json; charset=utf-8
{
"errors:" [
{
"code": <error code>,
"message": "<error message>",
"detail": ...
},
...
]
}
Thank you so much!
Cheers
The issue is modified RoundTripper in https://github.com/heroku/docker-registry-client makes a redirect call to registry after getting authentication token.when it tries to redirect call to registry ,body is empty because http.request body is a buffer and once it is read in first call,buffer becomes empty and there is no body content to send in redirect call.
To fix it:
if req.Method == "PUT" || req.Method == "POST" {
if req.Body != nil {
reUseBody, _ = req.GetBody()
}
}
add this in https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L17 and
if req.Method == "PUT" || req.Method == "POST" {
if reUseBody != nil {
req.Body = reUseBody
}
}
in here https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L72
and change this function signature https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L71
to accept extra argument.
func (t *TokenTransport) retry(req *http.Request, token string, reUseBody io.ReadCloser)
In a rest api, when the body is set to "{}", the json Decoder will not generate an error. This makes it necessary to check if the target struct is still nil.
I need to check if the library is supposed to work like this, or if this is an issue with it.
// Client Side this request
req, err := http.NewRequest("POST", "url", strings.NewReader("{}") )
// Curl equivalent:
curl -X POST -d '{}' http://api:8080/r/primitives/multiply
// Server side
type Operands struct {
Values []float64 `json:"Values"`
}
func handler(req *http.Request, res *http.Response) (string, error) {
operands := Operands{}
err := json.NewDecoder(req.Body).Decode(&operands)
if err != nil {
res.StatusCode = 400
res.Status = http.StatusText(res.StatusCode)
http.StatusText(res.StatusCode)
return "", err
}
operands.Values[0] // It will fail here.
}
Edit 1: The decoder works fine with and empty body "" with the error being generated, and works fine with a correct body like this one: {"Values" : [ 5.0 , 2.0 ]}
Edit 2: The issue here is that with a "{}" body, it will not return an error when decoding, instead it will keep the target struct as nil.
{} is just an empty Json object, and it will decode fine to your Operandsstruct, as the struct is not required to have anything in the Operands array.
You need to validate that yourself, e.g.
err := json.NewDecoder(req.Body).Decode(&operands)
if err != nil || len(operands.Values) == 0{
How can I fetch the json response from the POST method? Currently I'm only able to fetch Status - 401 Unauthorized and StatusCode - 401
func postUrl(url string, byt []byte) (*http.Response, error) {
tr := &http.Transport{
DisableCompression: true,
}
client := &http.Client{Transport: tr, Timeout: 10 * time.Second}
req, err := http.NewRequest("POST", url, bytes.NewBuffer(byt))
req.Header.Set("X-Custom-Header", "myvalue")
req.Header.Set("Content-Type", "application/json")
req.Header.Add("Authorization", "Basic "+basicAuth("username", "password"))
resp, err := client.Do(req)
return resp, err
}
Above code produces the output:
{
"errorMessages": [
"You do not have the permission to see the specified issue.",
"Login Required"
],
"errors": {}
}
The way to read the response (if there is one) is the same regardless of what status you get.
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
As Frank said, regardless of the status code in the response you can simply read its body to use whatever content it has.
Particularly for the case of a JSON message, you have two options depending on whether you know the JSON message structure in advance (or want your code to depend on it).
If you know the structure and are ok with hard-coding it (plus you gain some type safety and better client code) you can have:
type ErrorResponse struct {
Messages []string `json:"errorMessages"`
}
And then when you detect an error status code unmarshal the response body as that struct:
if resp.StatusCode % 100 != 2 {
var error ErrorResponse
err := json.Unmarshall(resp.Body, &error)
// check err != nil ...
// user error.ErrorMessages for whatever you want
}
Alternatively if you don't want to depend on the JSON structure (to some degree) you can try to unmarshall it to a map[string]interface{} and try to use that in the generic way you think you can (generally not very useful).
This question is not related to http responses and http methods. Decode json string (wich is http response body in that case) with json decoder.
Simple example
(not directly related to your code snippet)
type Transition struct {
Transition map[string]int
}
func main() {
resp, err := postUrl(url, byt)
if err != nil {
log.Fatal(err)
}
var trans Transition
decoder := json.NewDecoder(resp.Body)
if err := decoder.Decode(&trans); err != nil {
log.Fatal(err)
}
fmt.Printf("%+v\n", trans)
}
I am trying to make a Json web token authentication system with Go however I cant seem to get the parsing of the web token working.
The error occurs in the following function.
func RequireTokenAuthentication(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
authBackend := InitJWTAuthenticationBackend()
jwtString := req.Header.Get("Authorization")
token, err := jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
log.Println("Unexpected signing method")
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
} else {
log.Println("The token has been successfully returned")
return authBackend.PublicKey, nil
}
})
log.Println(token)
log.Println(token.Valid)
if err == nil && token.Valid && !authBackend.IsInBlacklist(req.Header.Get("Authorization")) {
next(rw, req)
} else {
rw.WriteHeader(http.StatusUnauthorized)
log.P
rintln("Status unauthorized RequireTokenAuthentication")
}
}
returns the following log
[negroni] Started GET /test/hello
2016/09/13 01:34:46 &{Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzM5NzQ4OTAsImlhdCI6MTQ3MzcxNTY5MCwic3ViIjoiIn0.mnwEwdR8nuvdLo_4Ie43me7iph2LeSj1uikokgD6VJB7isjFPShN8E7eQr4GKwuIiLTi34_i6iJRpmx9qrPugkzvsoxX44qlFi6M7FDhVySRiYbBQwTCvKCpvhnsK8BHJyEgy813aaxOMK6sKZJoaKs5JYUvnNZdNqmENYj1BM6FdbGP-oLHuR_CJK0Pym1NMhv9zLI1rpJOGu4mfj1t4tHYZAEGirPnzYMamtrK6TyEFE6Xi4voEEadq7hXvWREg6wNSQsYgww8uOaIWLy1yLbhTkPmT8zfRwLLYLqS_UuZ0xIaSWO1mF2plvOzz1WlF3ZEHLS31T1egB1XL4WTNQe <nil> map[] <nil> false}
2016/09/13 01:34:46 false
2016/09/13 01:34:46 Status unauthorized RequireTokenAuthentication
[negroni] Completed 401 Unauthorized in 71.628ms
and here is the cURL that I am using to initiate it
curl -H "Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzM5NzQ4OTAsImlhdCI6MTQ3MzcxNTY5MCwic3ViIjoiIn0.mnwEwdR8nuvdLo_4Ie43me7iph2LeSj1uikokgD6VJB7isjFPShN8E7eQr4GKwuIiLTi34_i6iJRpmx9qrPugkzvsoxX44qlFi6M7FDhVySRiYbBQwTCvKCpvhnsK8BHJyEgy813aaxOMK6sKZJoaKs5JYUvnNZdNqmENYj1BM6FdbGP-oLHuR_CJK0Pym1NMhv9zLI1rpJOGu4mfj1t4tHYZAEGirPnzYMamtrK6TyEFE6Xi4voEEadq7hXvWREg6wNSQsYgww8uOaIWLy1yLbhTkPmT8zfRwLLYLqS_UuZ0xIaSWO1mF2plvOzz1WlF3ZEHLS31T1egB1XL4WTNQe" http://localhost:5000/test/hello
I have also tried curl without Bearer
curl -H "Authorization:eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzM5NzQ4OTAsImlhdCI6MTQ3MzcxNTY5MCwic3ViIjoiIn0.mnwEwdR8nuvdLo_4Ie43me7iph2LeSj1uikokgD6VJB7isjFPShN8E7eQr4GKwuIiLTi34_i6iJRpmx9qrPugkzvsoxX44qlFi6M7FDhVySRiYbBQwTCvKCpvhnsK8BHJyEgy813aaxOMK6sKZJoaKs5JYUvnNZdNqmENYj1BM6FdbGP-oLHuR_CJK0Pym1NMhv9zLI1rpJOGu4mfj1t4tHYZAEGirPnzYMamtrK6TyEFE6Xi4voEEadq7hXvWREg6wNSQsYgww8uOaIWLy1yLbhTkPmT8zfRwLLYLqS_UuZ0xIaSWO1mF2plvOzz1WlF3ZEHLS31T1egB1XL4WTNQe" http://localhost:5000/test/hello
The error is occurring because the token is invalid token.Valid = false I have generated it using the following process.
Here is the router
router.HandleFunc("/token-auth", controllers.Login).Methods("POST")
Here is the login controller
func Login(w http.ResponseWriter, r *http.Request) {
requestUser := new(models.User)
decoder := json.NewDecoder(r.Body)
decoder.Decode(&requestUser)
responseStatus, token := utils.Login(requestUser) //here the util file seen below is used
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(responseStatus)
w.Write(token)
}
This is the util file
func Login(requestUser *models.User) (int, []byte) {
authBackend := authentication.InitJWTAuthenticationBackend()
if authBackend.Authenticate(requestUser) {
token, err := authBackend.GenerateToken(requestUser.UUID)
if err != nil {
return http.StatusInternalServerError, []byte("")
} else {
response, _ := json.Marshal(parameters.TokenAuthentication{token})
return http.StatusOK, response
}
}
return http.StatusUnauthorized, []byte("")
}
and here is the method used to generate the token
func (backend *JWTAuthenticationBackend) GenerateToken(userUUID string) (string, error) {
token := jwt.New(jwt.SigningMethodRS512)
claims := token.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * time.Duration(settings.Get().JWTExpirationDelta)).Unix()
claims["iat"] = time.Now().Unix()
claims["sub"] = userUUID
tokenString, err := token.SignedString(backend.privateKey)
if err != nil {
panic(err)
return "", err
}
return tokenString, nil
}
How do I fix the Token Parsing system so that the token is valid?
If you need any additional information I would be more than happy to make an edit with the respective information.
Thank
The error returned by jwt.Parse() says
tokenstring should not contain 'bearer '
So if you remove "Bearer ":
jwtString = strings.Split(jwtString, "Bearer ")[1]
you get a bit further
The token has been successfully returned
however now there's a new error:
key is of invalid type
Sorry it's not a complete answer!
key is of invalid type
type in this context is referring to the dynamic data-type in Go.
For SigningMethodRSA, the public key must be of type *rsa.PublicKey which can be constructed by calling jwt.ParseRSAPublicKeyFromPEM().
The key value returned to the parser might be created with something like:
keyStruct, _ := jwt.ParseRSAPublicKeyFromPEM(myPublicKeyString)
See:
https://github.com/dgrijalva/jwt-go#signing-methods-and-key-types
https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA
https://godoc.org/github.com/dgrijalva/jwt-go#ParseRSAPublicKeyFromPEM
Related:
How to generate JWT token always through invalid key type error