How to validate headers and body in Gin-Gonic? - http

I have a Gin program. When a request comes, I want all of the fields of the variable data (type ProductCreate) to have values: UserId (from headers) and Name, Price (from JSON body). I used the below code and it works:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type ProductCreate struct {
UserId int `header:"user-id"`
Name string `json:"name"`
Price int `json:"price"`
}
func main() {
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
var data ProductCreate
// bind the headers to data
if err := c.ShouldBindHeader(&data); err != nil {
c.JSON(400, err.Error())
return
}
// bind the body to data
if err := c.ShouldBindJSON(&data); err != nil {
c.JSON(400, err.Error())
return
}
c.JSON(200, data)
})
r.Run(":8080")
}
After that, I wanted to make sure that fields must be provided so I edited the ProductCreate struct like this:
type ProductCreate struct {
UserId int `header:"user-id" binding:"required"`
Name string `json:"name" binding:"required"`
Price int `json:"price" binding:"required"`
}
Then it raised an unexpected error when I tested it again:
Key: 'ProductCreate.Name' Error:Field validation for 'Name' failed on the 'required' tag\nKey: 'ProductCreate.Price' Error:Field validation for 'Price' failed on the 'required' tag
I realized the error happened at this:
// bind the headers to data
if err := c.ShouldBindHeader(&data); err != nil {
c.JSON(400, err.Error())
return
}
Is there any solution to resolve my problem?

Can you try this?
curl --location --request POST 'http://localhost:8080/product' \
--header 'user-id: 20' \
--data-raw '{
"name": "sr"
}'
I tried your code and it works perfectly.
{
"UserId": 20,
"name": "sr",
"price": 0
}
Gin version: github.com/gin-gonic/gin v1.8.1 // indirect
Soln:
package main
import (
"github.com/gin-gonic/gin"
)
type ProductCreate struct {
Name *string `json:"name" binding:"required"`
Price *int `json:"price" binding:"required"`
}
type Header struct {
UserId *int `header:"user-id" binding:"required"`
}
func main() {
r := gin.Default()
r.POST("/product", func(c *gin.Context) {
data := &ProductCreate{}
header := &Header{}
// bind the headers to data
if err := c.ShouldBindHeader(header); err != nil {
c.JSON(400, err.Error())
return
}
// bind the body to data
if err := c.ShouldBindJSON(data); err != nil {
c.JSON(400, err.Error())
return
}
c.JSON(200, data)
})
r.Run(":8080")
}

Related

io.Copy fails to copy all data to http.ResponseWriter

In the following codes, s.getFile gets file from S3 and return a struct of io.ReadCloser and ContentLength.
WriteResultesponse write the file to http.ResponseWriter.
But *reader.ContentLength sometimes is different from actualContentLength.
Any idea why? Thanks
s3Ctx, closeS3 := context.WithTimeout(ctx, xxx) // 1 hour
defer closeS3()
// directly stream result from locations
for _, location := range Locations {
reader, err := s.getFile(s3Ctx, xxx)
// reader is
//struct {
// Data io.ReadCloser
// ContentLength *int64
//}
if err != nil {
return err
}
actualContentLength, err := WriteResultesponse(params.Writer, ResultResponse{
Data: reader.Data,
})
}
// WriteResultResponse streams the result data to the user.
func WriteResultResponse(w http.ResponseWriter, resultResp ResultResponse) (int64, error) {
w.Header().Set("Content-Type", "text/plain")
// resultResp.Data is io.ReadCloser
defer resultResp.Data.Close()
return io.Copy(w, resultResp.Data)
}
UPDATE
How about
if f, ok := params.Writer.(http.Flusher); ok {
f.Flush()
}
?

How to get parameters in POST request

I am trying to get the parameters made in a POST request, but I am not able to make it, my code is:
package main
import (
"fmt"
"log"
"net/http"
)
func main() {
http.HandleFunc("/", hello)
fmt.Printf("Starting server for testing HTTP POST...\n")
if err := http.ListenAndServe(":8080", nil); err != nil {
log.Fatal(err)
}
}
func hello(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/" {
http.Error(w, "404 not found.", http.StatusNotFound)
return
}
switch r.Method {
case "POST":
// Call ParseForm() to parse the raw query and update r.PostForm and r.Form.
if err := r.ParseForm(); err != nil {
fmt.Fprintf(w, "ParseForm() err: %v", err)
return
}
name := r.Form.Get("name")
age := r.Form.Get("age")
fmt.Print("This have been received:")
fmt.Print("name: ", name)
fmt.Print("age: ", age)
default:
fmt.Fprintf(w, "Sorry, only POST methods are supported.")
}
}
I am making the POST request in the terminal as follows:
curl -X POST -d '{"name":"Alex","age":"50"}' localhost:8080
And then the output is:
This have been received:name: age:
Why it is not taking the parameters? What I am doing wrong?
As you pass your body as a json object, you better define a Go struct matching that object and decode the request body to the object.
type Info struct {
Name string
Age int
}
info := &Info{}
if err := json.NewDecoder(r.Body).Decode(info); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
_ = json.NewEncoder(w).Encode(info)
You can find the whole working code here.
$ curl -X POST -d '{"name":"Alex","age":50}' localhost:8080
This POST request is working fine now.
You could modify the Go struct and also the response object as you like .

How to return Document Index Name Value

This is a GoLang, Firebase AdminSDK question.
This example works to iterate through all of the documents in a FireStore DB.
How can I get the Document Name?
To put another way: If the collection name is JohnyCollection, and JohnyCollection has 20 Documents called (Document1, Document2.... Document20), how do I get the document name in golang Code?
//========================================
package main
import (
"context"
"fmt"
"log"
"firebase.google.com/go"
"google.golang.org/api/iterator"
"google.golang.org/api/option"
)
func check(e error) {
if e != nil {
panic(e)
}
}
func main() {
ctx := context.Background()
sa := option.WithCredentialsFile("./scai-qit-fb-adminsdk.json")
app, err := firebase.NewApp(ctx, nil, sa)
if err != nil {
log.Fatalf("error initializing app: %v\n", err)
}
client, err := app.Firestore(ctx)
if err != nil {
log.Fatal(err)
}
defer client.Close()
iter := client.Collection("COMPLEX_NONACS").Documents(ctx)
for {
doc, err := iter.Next()
if err == iterator.Done {
break
}
if err != nil {
log.Fatalf("Failed to iterate: %v", err)
}
//This part works. WIll return a Map of each Document
fmt.Println("--------------------------/n")
fmt.Println(doc.Data())
// This is the question. How do I get the INDEX name of the Document?
// something like...
fmt.Println(doc.Index_value_or_something_that_returns_IndexName())
// for example...
// {
// "ABC":{"line1":"yabba dabba","line2":"dingo dong"},
// "DEF":{"line1":"hooty tooty","line2":"blah blah"}
// }
// How to just get the "ABC" and "DEF"
}
}
You can get the document ID from the DocumentSnapshot, by first looking up the DocumentRef:
fmt.Println(doc.Ref.ID)
See the reference docs for DocumentSnapshot and DocumentRef.

How to reference multiple libs with same functions and switch between them inline in Go

I am wondering how I can do something similar to this. I currently have multiple packages with that same structure and functions but they actually retrieve values from multiple APIs. I am also loading in a config that has an array with parameters to use one of those packages per array item.
I am wondering how I can create a variable that uses one of those packages based on the config value. Hopefully this is clear enough. Here is pseudo code that I have written up to explain. Thanks in advance
package main
import (
"errors"
"flag"
"os"
"project/lib"
"project/morelib"
"project/extralib"
"fmt"
"math"
"math/rand"
"time"
)
func stuff(info RunInfo) (err error) {
apiKey:= "stuff1" // actually in the config
apiSecret := "stuff2" // actually in the config
variable := lib.New(apiKey, apiSecret) //returns *Lib struct
//this is where I have to comment out the other libs and uncomment them as needed
// variable := morelib.New(apiKey, apiSecret)
// variable := extralib.New(apiKey, apiSecret)
//trying to switch between libraries like this or in a switch statement
if info.libname == "lib"{
variable = lib.New(apiKey, apiSecret) //.New("", "") returns *Lib struct
}else if info.libname == "morelib"{
variable = morelib.New(apiKey, apiSecret) //.New("", "") returns *MoreLib struct
}else if info.libname == "extralib"{
variable = extralib.New(apiKey, apiSecret) //.New("", "") returns *ExtraLib struct
}else{
err = errors.New("there was an error with the value.....")
return err
}
mystuffs, err := variable.GetBalance("USD")
if err != nil {
fmt.Println(err)
return
}
fmt.Printf("mystuff value: %v", mystuffs.value)
return
}
type RunInfo struct{
libname string
//other stuff
}
func main() {
//load from config with array
config := config.Load()
for i:=0; i<compare; i++{
var runInfo RunInfo
runInfo.libname = config.libname
stuff(runInfo)
}
}
pseudo lib code:
func New(apiKey, apiSecret string) *Lib {
client := NewClient(apiKey, apiSecret)
return &Lib{client}
}
func NewClient(apiKey, apiSecret string) (c *client) {
return &client{apiKey, apiSecret, &http.Client{}, false}
}
type Lib struct {
client *client
}
type client struct {
apiKey string
apiSecret string
httpClient *http.Client
debug bool
}
func (b *Lib) GetBalance(currency string) (balance Balance, err error) {
payload, err := json.Marshal(BalanceParams{Currency: currency})
if err != nil {
return
}
r, err := b.client.do("POST", "GetBalance", string(payload), true)
if err != nil {
return
}
var response jsonResponse
if err = json.Unmarshal(r, &response); err != nil {
return
}
if err = handleErr(response); err != nil {
return
}
err = json.Unmarshal(response.Result, &balance)
return
}
Use and if statement as in the question, a switch statement or a map.
I assume that the type returned by the New function is the following interface:
type GetStuffer interface
GetStuff(string) (Stuff, error)
}
The switch statement is:
var variable GetStuffer
switch info.CompareVal {
case "lib":
variable = lib.New(string1, string2)
case "morelib":
variable = morelib.New(string1, string2)
case "extralib":
variable = extralib.New(string1, string2)
default:
return errors.New("there was an error with the value.....")
}
mystuffs, err := variable.GetMyStuff()
if err != nil {
fmt.Println(err)
return
}
For the map, initialize a package level variable with the map:
var m = map[string]func(string,string) GetStuffer {
"lib": lib.New,
"morelib": morelib.New,
"extralib": extralib.New,
}
and use it like this:
fn := m[info.CompareValue]
if m == nil {
return errors.New("there was an error with the value.....")
}
variable := fn(string1, string2)
mystuffs, err := variable.GetMyStuff()
if err != nil {
fmt.Println(err)
return
}
If the assumption above about the return type is not correct, then there are two options. The first and likely the simplest is to modify New functions to return type GetStuffer. If that's not possible, then write little adaptor functions:
var m = map[string]func(string,string) GetStuffer {
"lib":func(s1, s2 string) GetStuffer { return lib.New(s1, s2) }
"morelib":func(s1, s2 string) GetStuffer { return morelib.New(s1, s2) }
"extralib":func(s1, s2 string) GetStuffer { return extralib.New(s1, s2) }
}
Why don't you define an interface that's only one function? In your example would be
type Stuffer interface {
GetMyStuff(string) (Stuff, error)
}
Then you declare your variable as type Stuffer.

Golang: returning pointers and derefrencing

So I have a function getToken()
func getToken() jwt.MapClaims {
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkYW0iLCJwYXNzd29yZCI6InRlc3QiLCJpYXQiOjE0ODcyMDY2OTIsImV4cCI6MTUxODc2NDI5Mn0.6LQo_gRwXiFBvNIJOwtf9UuxoQMZZ3XNILTnU-46-Zg"
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
}
hmacSampleSecret := []byte("supersecretkittysecret")
return hmacSampleSecret, nil
})
if err != nil {
println("error")
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
return claims
} else {
return nil
}
}
Then the corresponding call:
res := getToken()
println(res["username"])
Why is res["username"] equal to two memory addresses (0x2b3c20,0xc420075420)? This should just be a string like adam. I have also tried func getToken() *jwt.MapClaims and return &claims, but this still did not help.
You should try using fmt.Println instead of println. Here's an example of printing a map using println vs fmt.Println
package main
import (
"fmt"
)
func foo() map[string]string {
return map[string]string{
"k": "value",
}
}
func main() {
res := foo()
println("Output from println:", res) // prints pointer address
fmt.Println("Output from fmt.Println: ", res) // prints the map
}
https://play.golang.org/p/gCNqng3KEE
Output:
Output from println: 0x10432200
Output from fmt.Println: map[k:value]

Resources