Postman JSON Response empty - json

I am using go with the echo framework. Created a Post Request to a Docker Postgre database. Everything is working fine when I use Params in Postman to add a new User to the database, but when I am trying to send it as a Body/raw JSON, the response is an empty JSON. Anybody can point me in the right direction?
Working params:
firstname=Dennis
lastname=Liga
JSON I am trying to send:
{
"firstname": "Dennis",
"lastname": "Liga"
}
main.go
e.POST("/addPerson", routes.AddPerson)
routes.go
func AddPerson(c echo.Context) error {
ctx := context.Background()
db := connectors.GetDbConnection()
firstname := c.QueryParam("firstname")
lastname := c.QueryParam("lastname")
queries := postgres.New(db)
insertedPerson, err := queries.CreatePersons(ctx, postgres.CreatePersonsParams{
Firstname: firstname,
Lastname: lastname,
})
if err != nil {
log.Errorf("Failed to insert a person %v", err)
return err
}
fmt.Println(insertedPerson)
return c.JSONPretty(http.StatusOK, insertedPerson, " ")
}

Related

Golang how to pass username and password as JSON body to Post API

Using a post API request I want to receive a JWT token for the response.
For that, I want to send the username and password as JSON body input to the API. The username and password should be taken dynamically from the user as a JSON payload.
Currently, I am hardcoding the user name and password using strings.NewReader which is not suitable as this is a web application and many users will be using it.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"log"
"net/http"
"os"
"strings"
"time"
"github.com/joho/godotenv"
)
var authToken string
type authorization struct {
AuthorizationToken string `json:"authorizationToken"`
ExpiresTimestamp string `json:"expiresTimestamp"`
}
func main() {
enverr := godotenv.Load(".env")
if enverr != nil {
fmt.Println("Error loading .env file")
os.Exit(1)
}
token()
}
func token() {
authorizationUrl := os.Getenv("authorizationUrl")
requestBody := strings.NewReader(`
{
"name" : "testuser",
"pwd" : "testpwd",
"hName" : "hname"
}
`)
response, err := http.Post(authorizationUrl, "application/json", requestBody)
if err != nil {
panic(err)
}
defer response.Body.Close()
content, _ := ioutil.ReadAll(response.Body)
var result authorization
if err := json.Unmarshal(content, &result); err != nil { // Parse []byte to the go struct pointer
fmt.Println("Can not unmarshal JSON")
}
authToken := result.AuthorizationToken
fmt.Println(PrettyPrint(authToken))
}
func PrettyPrint(i interface{}) string {
s, _ := json.MarshalIndent(i, "", "\t")
return string(s)
}
You can use http requests body (for POST request)
type Credentials struct {
Username string `json:"username"`
Password string `json:"password"`
}
c := Credentials{}
decoder := json.NewDecoder(r.Body)
defer r.Body.Close()
err: = decoder.Decode(&c)
if err!=nil{
// handle it
}
// use it
fmt.Println(c.Username, c.Password)
If you are intending to distribute the application as an executable (aka command line in the Unix world), you can either:
Provide the username and password as program arguments, os.Args (omitting unchanged code):
var inputTemplate = `
{
"name" : "USER",
"pwd" : "PWD",
"hName" : "hname"
}
`
func token() {
authorizationUrl := os.Getenv("authorizationUrl")
requestBody := strings.Replace(strings.Replace(inputTemplate, "PWD", os.Args[2], -1), "USER", os.Args[1], -1)
response, err := http.Post(authorizationUrl, "application/json", requestBody)
// ...
}
You should then be able to run your program (once compiled): ./filename user password
Or as better alternative, having sensitive data involved, have the username and password input from standard input with password being echoed (hiding the characters):
import (
// ...
"syscall"
"golang.org/x/term"
)
var inputTemplate = `
{
"name" : "USER",
"pwd" : "PWD",
"hName" : "hname"
}
`
func token() {
reader := bufio.NewReader(os.Stdin)
fmt.Print("Enter username: ")
username := reader.ReadString('\n') // handle error
fmt.Print("Enter password: ")
pwdbytes := term.ReadPassword(int(syscall.Stdin))
password := string(pwdbytes)
authorizationUrl := os.Getenv("authorizationUrl")
requestBody := strings.Replace(strings.Replace(inputTemplate, "PWD", password, -1), "USER", username, -1)
response, err := http.Post(authorizationUrl, "application/json", requestBody)
// ...
}

Golang Parsing Request body throws, contains unknown field "password"?

I'm not sure what is causing this, but the reason it throws it is when I call dec.DisallowUnknownFields() it somehow thinks that password isn't a field in the User struct/request body. Albeit, the password JSON is "-". So I thought originally to change the JSON to "password" for struct field Password but that throws an
internal server error: illegal base64 data at input byte 4
Request Body in Postman
{
"firstname": "George",
"lastname": "Costanza",
"email": "george#gmail.com",
"phone": "703-123-4567",
"password": "abc12"
}
I also tried to change the type of password to string, which also didn't work but arguably would be the wrong solution because we should store passwords as hashes in the DB. So, at this point I've run out of places to turn to as to why this is happening...
models.go
type User struct {
ID primitive.ObjectID `json:"_id,omitempty" bson:"_id,omitempty"`
FirstName string `json:"firstname"`
LastName string `json:"lastname"`
Email string `json:"email"`
Phone string `json:"phone"`
Password []byte `json:"-"`
Created time.Time `json:"-"`
Active bool `json:"-"`
Address Address `json:"address,omitempty"`
}
Helpers.go
type malformedRequest struct {
status int
msg string
}
func (mr *malformedRequest) Error() string {
return mr.msg
}
func (app *appInjection) decodeJSONBody(w http.ResponseWriter, r *http.Request, dst interface{}) error {
if r.Header.Get("Content-Type") != "" {
value, _ := header.ParseValueAndParams(r.Header, "Content-Type")
if value != "application/json" {
msg := "Content-Type header is not application/json"
return &malformedRequest{status: http.StatusUnsupportedMediaType, msg: msg}
}
}
r.Body = http.MaxBytesReader(w, r.Body, 1048576)
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
err := dec.Decode(&dst)
if err != nil {
var syntaxError *json.SyntaxError
var unmarshalTypeError *json.UnmarshalTypeError
switch {
case errors.As(err, &syntaxError):
msg := fmt.Sprintf("Request body contains badly-formed JSON (at position %d)", syntaxError.Offset)
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case errors.Is(err, io.ErrUnexpectedEOF):
msg := fmt.Sprintf("Request body contains badly-formed JSON")
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case errors.As(err, &unmarshalTypeError):
msg := fmt.Sprintf("Request body contains an invalid value for the %q field (at position %d)", unmarshalTypeError.Field, unmarshalTypeError.Offset)
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case strings.HasPrefix(err.Error(), "json: unknown field "):
fieldName := strings.TrimPrefix(err.Error(), "json: unknown field ")
msg := fmt.Sprintf("Request body contains unknown field %s", fieldName)
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case errors.Is(err, io.EOF):
msg := "Request body must not be empty"
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
case err.Error() == "http: request body too large":
msg := "Request body must not be larger than 1MB"
return &malformedRequest{status: http.StatusRequestEntityTooLarge, msg: msg}
default:
return err
}
}
err = dec.Decode(&struct{}{})
if err != io.EOF {
msg := "Request body must only contain a single JSON object"
return &malformedRequest{status: http.StatusBadRequest, msg: msg}
}
return nil
}
Handlers.go
func (app *appInjection) RegisterUser(w http.ResponseWriter, r *http.Request) {
// Guide addressing headers, syntax error's, and preventing extra data fields
// https://www.alexedwards.net/blog/how-to-properly-parse-a-json-request-body
w.Header().Set("Content-Type", "application/json")
var newUser models.User
//Parse the form data
//err := json.NewDecoder(r.Body).Decode(&newUser)
err := app.decodeJSONBody(w, r, &newUser)
if err != nil {
var mr *malformedRequest
if errors.As(err, &mr) {
http.Error(w, mr.msg, mr.status)
//app.clientError(w, mr.status)
} else {
log.Println(err.Error())
//app.clientError(w, http.StatusInternalServerError)
http.Error(w, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
}
return
}
//TODO: Validate the form
//If there is no error and the form is validated, create a new user from http request
//Insert the new user into the database
uid, _ := app.user.Insert(
newUser.FirstName,
newUser.LastName,
newUser.Email,
newUser.Phone,
string(newUser.Password))
json.NewEncoder(w).Encode("Record Inserted")
json.NewEncoder(w).Encode(uid)
}
User.go
func (u *UserFunctions) Insert(firstname, lastname, email, phone, password string) (primitive.ObjectID, error) {
//Insert user to the database
userCollection := u.CLIENT.Database("queue").Collection("users")
var user models.User
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 12)
if err != nil {
object, _ := primitive.ObjectIDFromHex("")
return object, err
}
user.FirstName = firstname
user.LastName = lastname
user.Email = email
user.Phone = phone
user.Password = hashedPassword
user.Created = time.Now().UTC()
user.Active = true
//Insert the user into the database
result, err := userCollection.InsertOne(context.TODO(), user)
if err != nil {
fmt.Println(err)
}
//Check ID of the inserted document
insertedID := result.InsertedID.(primitive.ObjectID)
//fmt.Println(insertedID)
return insertedID, nil
}
When I run the request to sign up a user in Postman it throws the message
Request body contains unknown field "password"
As folks already linked extensively, since JSON cannot contain bytes directly, the Go Json parser expects any []byte objects to be stored in Base64 in the input data.
Your problem, therefore, is not necessarily on the Go end. Your problem is a mismatch with what you told Go to expect - []byte, Base64 encoded - and what you told the client to send - "abc12" - which is not a valid base64 value.
One simple solution for your stated case would be to send a base64 encoded version of the same string:
{
"firstname": "George",
"lastname": "Costanza",
"email": "george#gmail.com",
"phone": "703-123-4567",
"password": "YWJjMTIK"
}
But I'd like to dig just a little bit deeper.
string [...] arguably would be the wrong solution because we should store passwords as hashes in the DB
You might store passwords hashed into bytes in the database, but that's not what's coming int from curl . So to me, your real problem is that you took 2 different kinds of data - user provided password and DB hashed representation - and tried to mash them into the same data space, which has absolutely no value. After all, if you were setting a password, the current hash, if any, is irrelevant, and if you're checking the password, you'd be checking what they inputted.
This is the solution I'd prefer:
type User struct {
... ... ...
HashedPassword []byte `json:"-"`
Password string `json:"password"`
}
A simple solution would be
var newUser struct {
models.User
Password string `json:"password"`
}
//Parse the form data
//err := json.NewDecoder(r.Body).Decode(&newUser)
err := app.decodeJSONBody(w, r, &newUser)

Is there any method similar to Golang's Scan() method (used for SQL) for Elasticsearch?

I am new to both Go and ES. I want to convert the following piece of code:
query = `SELECT roll_no, name, school FROM students where roll_no = $1`
err = db.QueryRowContext(ctx, query, roll_no).Scan(&Student.ID, &Student.Name, &Student.School)
into something like the following Elasticsearch query:
str = fmt.Sprintf(`{"query": { "match": { "roll_no" : %d } } }`, roll_no)
b := []byte(str)
// calls retrieve method, which is shown below
I am connecting to ES using HTTP calls, but the following code is showing http: panic serving [::1]:5574: EOF error while parsing.
func retrieve(url string, b []byte) ([]byte, error) {
request, _ := http.NewRequest("GET", url, bytes.NewBuffer(b))
request.Header.Set("Content-Type", "application/json; charset=UTF-8")
client := &http.Client{}
response, error := client.Do(request)
if error != nil {
panic(error)
}
defer response.Body.Close()
body, _ := ioutil.ReadAll(response.Body)
s := Student{}
error = json.NewDecoder(response.Body).Decode(&s)
if error != nil {
panic(error)
}
fmt.Printf("Student: %v", s)
return body, error
}
Is there anyway I can store it into an object by parsing?
How can I use Golang's Scan() method (normally used for SQL) in Elasticsearch?
Not at all.
Package database/sql needs a database driver and there isn't one for ES.

Wrapping GraphQL query inside JSON string to send a request to GraphQL endpoint

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))
}

Replying with a JSON on an open TCP socket

I have a server that successfully opens a connection with a second server. The second server performs an action and I am trying to get it to reply to the first server with a JSON on the same connection.
package main
import (
"fmt"
"net"
"encoding/json"
)
type NewContainerJSON struct {
Action string `json:"Action"`
ContainerName string `json:"ContainerName"`
BaseServer string `json:"BaseServer"`
CMS string `json:"CMS"`
WebsiteName string `json:"WebsiteName"`
DBrootPWD string `json:"DBrootPWD"`
DBadminUname string `json:"DBadminUname"`
DBadminPWD string `json:"DBadminPWD"`
}
func main() {
service := "127.0.0.1:8081"
tcpAddr, err := net.ResolveTCPAddr("tcp", service)
checkError(err)
listener, err := net.ListenTCP("tcp", tcpAddr)
checkError(err)
conn, err := listener.Accept()
checkError(err)
decoder := json.NewDecoder(conn)
encoder := json.NewEncoder(conn)
var b NewContainerJSON
err = decoder.Decode(&b)
checkError(err)
fmt.Println(b.Action)
if b.Action == "createNew" {
fmt.Println("This works")
resp := []byte("And here's our repomse")
conn.Write(resp)
c := NewContainerJSON {
Action: "createdNewContainer",
ContainerName: "Test",
BaseServer: "Test",
CMS: "Test",
WebsiteName: "Test",
DBrootPWD: "Test",
DBadminUname: "Test",
DBadminPWD: "Test",
}
encoder := json.NewEncoder(conn)
if err := encoder.Encode(c); err != nil {
fmt.Println("encode.Encode error: ", err)
}
conn.Write(c)
}
}
func checkError(err error) {
if err != nil {
fmt.Println("An error occurred: ", err.Error())
}
}
I get following error on the line conn.Write(c)
cannot use c (type NewContainerJSON) as type []byte in argument to conn.Write
Two questions:
1: What exactly is this error saying? It seems to be complaining that 'c' cannot be used as a Byte when using the conn.Write function but shouldn't the json.Encoder convert the JSON to a format the conn.Write can use?
2: How exactly can I return a JSON back to the first server using the open connection?
The encoder writes the JSON encoding of c to conn on this line:
if err := encoder.Encode(c); err != nil {
That's all you need to do. Delete the call to conn.Write(c).
The error message is telling you that the value of c cannot be used as the argument to Write because of a type mismatch. A NewContainerJSON is not a []byte.
You first write a string to the connection by
resp := []byte("And here's our repomse")
conn.Write(resp)
This will make it error-prone on the client side. You'll need to read exactly the same amount of data before employ the json decoder on this connection.
If a connection is used for json communication, all the messages on this stream should be json.
So if you want send a message to notify, encode that message too:
encoder.Encode(string(resp))