validation of formvalue data in golang - validation

I am getting data in the post request as a form data and i want to validate that data before i send that data to the Database query.
How can i do that.
I can see that i can use validator with json type by binding.
This is my game struct:
type Game struct {
Id string `json:"Id"`
Name string `json:"Name" validate:"required"`
Rating float32 `json:"Rating" `
TimesPlayed int `json:"TimesPlayed" validate:"required, number"`
ImagePath string `json:"ImagePath" validate:"required, file"`
Description string `json:"Description"`
Developer string `json:"Developer" validate:"required, min=5, max=20"`
CreationDate time.Time `json:"CreationDate"`
Version string `json:"Version"`
Tags []string `json:"Tags"`
Downloads int64 `json:"Downloads" validate:"number"` // firestore doesn't support uint64
DownloadLink string `json:"DownloadLink" validate:"required, uri"`
}
I have formvalue and i want to take advantage of validator to validate my formvalue.
Any guide on how can i do this?
Right now i am doing this without validation
func newGameHandler(c echo.Context) error {
// create new game model
// TODO need a security layer in between the form and our new game struct
newGame := models.Game{
Name: c.FormValue(NAME),
Developer: c.FormValue(DEVELOPER),
CreationDate: time.Now(),
Version: c.FormValue(VERSION),
}
// Add new game to database
_db, getDbErr := db.NewDatabaseFromEnv()
_, err := _db.AddGame(newGame)
// error handling
if getDbErr != nil {
return c.JSON(http.StatusInternalServerError, "Database connection error")
}
if err != nil {
return c.JSON(http.StatusInternalServerError, "Database add game error")
}
// TODO return successful game add
return c.JSON(http.StatusOK, "New game handler")
}
how can i add validation on this?

It's depends on what framework you are using but we have Go-validator for general purposes, also based on framework you are using you can use their validator too, like echo-validator or gin-validator.
You can add your custom validation function to them too.

Related

How can I skip a specific field from struct while inserting with gorm

I was trying to make a rest API for user registration and on that api there is a field named "gender", so I'm receiving that field as Struct but on user table there is no field for "gender". Is it possible to skip that "gender" field from struct while inserting with gorm?
This is my DataModel
package DataModels
type User struct {
Id uint `json:"id"`
Otp int `json:"otp"`
UserId string `json:"user_id"`
UserType string `json:"user_type"`
FullName string `json:"full_name"`
MobileNo string `json:"mobile"`
Email string `json:"email"`
Gender string `json:"gender"` // I want to skip this filed while inserting to users table
Password string `json:"password"`
}
func (b *User) TableName() string {
return "users"
}
This my Controller Function
func CreateUser(c *gin.Context) {
var user Models.User
_ = c.BindJSON(&user)
err := Models.CreateUser(&user) // want to skip that gender filed while inserting
if err != nil {
fmt.Println(err.Error())
c.AbortWithStatus(http.StatusNotFound)
} else {
c.JSON(http.StatusOK, user)
}
}
This is my model function for inserting
func CreateUser(user *User) (err error) {
if err = Config.DB.Create(user).Error; err != nil {
return err
}
return nil
}
GORM allows you to ignore the field with tag, use gorm:"-" to ignore the field
type User struct {
...
Gender string `json:"gender" gorm:"-"` // ignore this field when write and read
}
Offical doc details about Field-Level Permission
Eklavyas Answer is omitting gender always, not just on Create.
If I am correct you want to Skip the Gender field within the Registration. You can use Omit for that.
db.Omit("Gender").Create(&user)

Idiomatic way to update with GORM and echo-framework

Hi new in golang and trying to make rest API
I want to updating entity passing only needed datas with GORM and golang echo framework.
And I get this error :
"strconv.ParseUint: parsing \"ea78944c-a2a9-4813-86e7-10b199d0f002\": invalid syntax"
I use echo.Bind() func to bind formdata (I use postman) with my Club struct.
PS : I use xid to external id and keep my int id to interne work.
Expected :
I'm gonna find Club in my database by id (get with url param) with GORM and set it up in a new club struct.
After that, I bind my club struct with my formdata.
And finally save it with Save() GORM func. => get error
Reality :
I'm gonna find Club in my database by id (write hardcoding string id) with GORM and set it up in a new club struct.
After that, I bind my club struct with my formdata.
And finally save it with Save() GORM func. => works
Conclusion :
I don't know how to pass url parameter AND formdata in PUT method using echo.Bind()
Here my Club struct :
// Club struct
type Club struct {
Base
Name string `json:"name;" gorm:"type:varchar(255);not null" validate:"required"`
Slug string `json:"slug;" gorm:"type:varchar(255);unique;not null"`
Website string `json:"website;" gorm:"type:varchar(255)"`
Lat float32 `json:"lat;" gorm:"lat;" sql:"type:decimal(8,6);" validate:"required,numeric"`
Lng float32 `json:"lng;" gorm:"lng;" sql:"type:decimal(9,6);" validate:"required,numeric"`
Logo string `json:"logo;" gorm:"type:varchar(100)" validate:"required"`
Phone string `json:"phone;" gorm:"type:varchar(20)" validate:"required"`
}
My FindById func :
// GetClubByXID get club by xid
func GetClubByXID(c echo.Context, xid string) (*schemas.Club, error) {
club := new(schemas.Club)
if result := db.Where("xid = ?", xid).First(&club); result.Error != nil {
return nil, result.Error
}
return club, nil
}
And here my updating func :
func UpdateClub(c echo.Context) error {
xid := c.Param("id") // => doesn't work
// xid := "ea78944c-a2a9-4813-86e7-10b199d0f002" // => work
club, err := models.GetClubByXID(c, xid)
if err != nil {
return c.JSON(http.StatusNotFound, err)
}
if err := c.Bind(club); err != nil {
return c.JSON(http.StatusInternalServerError, err)
}
db.Save(&club)
return c.JSON(http.StatusOK, echo.Map{
"club": &club,
})
}
My updating route :
API.PUT("/updateClub/:id", handlers.UpdateClub) // => doesn't work
// API.PUT("/updateClub", handlers.UpdateClub) // => work
When I write with hardcoding my xid ea78944c-a2a9-4813-86e7-10b199d0f002 in my updating func it's work like a charm but I can't combine my url and my formdata with echo.Bind()
my err if I try http://localhost:1323/api/updateClub/ea78944c-a2a9-4813-86e7-10b199d0f002:
"strconv.ParseUint: parsing \"ea78944c-a2a9-4813-86e7-10b199d0f002\": invalid syntax"
Thanks for reading hope someone can help me :)
Can you try to c.Param("xid") instead of c.Param("id") and change the routing parameter as API.PUT("/updateClub/:xid", handlers.UpdateClub)

Reuse or cast structs for validation, API results, and DB saves in Go

I'm trying to write an API that will do different things with the data depending on its purpose.
API results - The API should expose certain fields to the user
Validation - Validation should handle different schemas e.g. login form doesn't require Name, but register does
Database - The database should save everything, including password etc. - if I try using the same struct for the API and DB with json:"-" or un-exporting the field the database save also ignores the field
Some sample code is below with a couple of comments in capitals to show where I ideally need to type cast to change the data. As they are different structs, they cannot be type cast, so I get an error. How can I fix this?
Alternatively, what is a better way of doing different things with the data without having lots of similar structs?
// IDValidation for uuids
type IDValidation struct {
ID string `json:"id" validate:"required,uuid"`
}
// RegisterValidation for register form
type RegisterValidation struct {
Name string `json:"name" validate:"required"`
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required,min=8"`
}
// UserModel to save in DB
type UserModel struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
Password string `json:"password,omitempty"`
Active bool `json:"active,omitempty"`
CreatedAt int64 `json:"created_at,omitempty"`
UpdatedAt int64 `json:"updated_at,omitempty"`
jwt.StandardClaims
}
// UserAPI data to display to user
type UserAPI struct {
ID string `json:"id,omitempty"`
Name string `json:"name"`
Email string `json:"email"`
Active bool `json:"active,omitempty"`
}
// register a user
func register(c echo.Context) error {
u := new(UserModel)
if err := c.Bind(u); err != nil {
return err
}
// NEED TO CAST TO RegisterValidation HERE?
if err := c.Validate(u); err != nil {
return err
}
token, err := u.Register()
if err != nil {
return err
}
return c.JSON(http.StatusOK, lib.JSON{"token": token})
}
// retrieve a user
func retrieve(c echo.Context) error {
u := IDValidation{
ID: c.Param("id"),
}
if err := c.Validate(u); err != nil {
return echo.NewHTTPError(http.StatusBadRequest, err.Error())
}
// NEED TO CAST USER TO UserAPI?
user, err := userModel.GetByID(u.ID)
if err != nil {
return err
}
return c.JSON(200, lib.JSON{"message": "User found", "user": user})
}

Is it possible to delete a field of a struct value at runtime?

I have the following struct:
type Record struct {
Id string `json:"id"`
ApiKey string `json:"apiKey"`
Body []string `json:"body"`
Type string `json:"type"`
}
Which is a Blueprint of a dynamoDB table. And I need somehow, delete the ApiKey, after using to check if the user has access to the giving record. Explaining:
I have and endpoint in my API where the user can send a id to get a item, but he needs to have access to the ID and the ApiKey (I'm using Id (uuid) + ApiKey) to create unique items.
How I'm doing:
func getExtraction(id string, apiKey string) (Record, error) {
svc := dynamodb.New(cfg)
req := svc.GetItemRequest(&dynamodb.GetItemInput{
TableName: aws.String(awsEnv.Dynamo_Table),
Key: map[string]dynamodb.AttributeValue{
"id": {
S: aws.String(id),
},
},
})
result, err := req.Send()
if err != nil {
return Record{}, err
}
record := Record{}
err = dynamodbattribute.UnmarshalMap(result.Item, &record)
if err != nil {
return Record{}, err
}
if record.ApiKey != apiKey {
return Record{}, fmt.Errorf("item %d not found", id)
}
// Delete ApiKey from record
return record, nil
}
After checking if the ApiKey is equal to the provided apiKey, I want to delete the ApiKey from record, but unfortunately that's not possible using delete.
Thank you.
There is no way to actually edit a golang type (such as a struct) at runtime. Unfortunately you haven't really explained what you are hoping to achieve by "deleting" the APIKey field.
General approaches would be:
set the APIKey field to an empty string after inspection, if you dont want to display this field when empty set the json struct tag to omitempty (e.g `json:"apiKey,omitempty"`)
set the APIKey field to never Marshal into JSON ( e.g ApiKey string `json:"-"`) and you will still be able to inspect it just wont display in JSON, you could take this further by adding a custom marshal / unmarshal function to handle this in one direction or in a context dependent way
copy the data to a new struct, e.g type RecordNoAPI struct without the APIKey field and return that after you have inspected the original Record
Created RecordShort structure without "ApiKey"
Marshalin Record
Unmarsaling Record to ShortRecord
type RecordShot struct {
Id string `json:"id"`
Body []string `json:"body"`
Type string `json:"type"`
}
record,_:=json.Marshal(Record)
json.Unmarshal([]byte(record), &RecordShot)

How to use Go's type alias to make own models work with protobufs?

I've got some REST API with my models defined as Go structs.
type User struct {
FirstName string
LastName string
}
Then I've got my database methods for getting data.
GetUserByID(id int) (*User, error)
Now I'd like to replace my REST API with https://github.com/twitchtv/twirp .
Therefore I started defining my models inside .proto files.
message User {
string first_name = 2;
string last_name = 3;
}
Now I've got two User types. Let's call them the native and the proto type.
I've also got a service defined in my .proto file which returns a user to the frontend.
service Users {
rpc GetUser(Id) returns (User);
}
This generates an interface that I have to fill in.
func (s *Server) GetUser(context.Context, id) (*User, error) {
// i'd like to reuse my existing database methods
u, err := db.GetUserByID(id)
// handle error
// do more stuff
return u, nil
}
Unfortunately this does not work. My database returns a native User but the interface requires a proto user.
Is there an easy way to make it work? Maybe using type aliases?
Thanks a lot!
One way you can solve your problem is by doing the conversion manually.
type User struct {
FirstName string
LastName string
}
type protoUser struct {
firstName string
lastName string
}
func main() {
u := db() // Retrieve a user from a mocked db
fmt.Println("Before:")
fmt.Printf("%#v\n", *u) // What db returns (*protoUser)
fmt.Println("After:")
fmt.Printf("%#v\n", u.AsUser()) // What conversion returns (User)
}
// Mocked db that returns pointer to protoUser
func db() *protoUser {
pu := protoUser{"John", "Dough"}
return &pu
}
// Conversion method (converts protoUser into a User)
func (pu *protoUser) AsUser() User {
return User{pu.firstName, pu.lastName}
}
The key part is the AsUser method on the protoUser struct.
There we simply write our custom logic for converting a protoUser into a User type we want to be working with.
Working Example
As #Peter mentioned in the comment section.
I've seen a project which made it with a custom Convert function. It converts the Protobuf to local struct via json.Unmarshal, not sure how's the performance but it's a way to go.
Preview Code PLAYGROUND
// Convert converts the in struct to out struct via `json.Unmarshal`
func Convert(in interface{}, out interface{}) error {
j, err := json.Marshal(in)
if err != nil {
return err
}
err = json.Unmarshal(j, &out)
if err != nil {
return err
}
return nil
}
func main() {
// Converts the protobuf struct to local struct via json.Unmarshal
var localUser User
if err := convert(protoUser, &localUser); err != nil {
panic(err)
}
}
Output
Before:
main.ProtoUser{FirstName:"John", LastName:"Dough"}
After:
main.User{FirstName:"John", LastName:"Dough"}
Program exited.

Resources