Доброго времени суток! Изучаю язык golang, выбрал фреймворк echo. В принципе все понятно кроме некоторых фундаментальных вещей и синтаксиса. Решил изучать на реальном проекте, так как считаю что это самый эффективный способ. Взялся за проект, обычный сайт + админка, все было хорошо пока не дошел до HTML, а именно форм, требуется авторизовывать пользователя и выдавать ему токен, все отлично работает с JSON, но для того чтобы был SEO приходится рендерить HTML поэтому решил не использовать JS Front end framework'и типа VUE, и сделать все на HTML5, добрался до форм и не могу понять как спарсить данные с формы и засунуть их в MONGODB.
Создал модель
package model
import (
mongopagination "github.com/gobeam/mongo-go-pagination"
"go.mongodb.org/mongo-driver/bson/primitive"
)
type User struct {
*UserInput `bson:",inline"`
ID primitive.ObjectID `json:"id" xml:"id" form:"id" bson:"_id,omitempty"`
}
type UserInput struct {
FirstName string `json:"firstName" xml:"firstName" form:"firstName" bson:"firstName" validate:"required"`
LastName string `json:"lastName" xml:"lastName" form:"lastName" bson:"lastName" validate:"required"`
Email string `json:"email" xml:"email" form:"email" bson:"email" validate:"required,email"`
Password string `json:"password,omitempty" form:"password,omitempty" xml:"password,omitempty" bson:"password" validate:"required"`
}
type LoginInput struct {
Email string `json:"email" xml:"email" bson:"email" validate:"required,email"`
Password string `json:"password" xml:"password" bson:"password" validate:"required"`
}
type PagedUser struct {
Data []User `json:"data" xml:"data"`
PageInfo mongopagination.PaginationData `json:"pageInfo" xml:"pageInfo"`
}
С формы public/signup.html
{{define "signup"}}
{{template "head"}}
{{template "header"}}
<div class="signup">
<div class="container">
<div class="row">
<div class="col-md-6">
<form method="POST" action="/api/v1/signup">
<label>Email</label>
<input email="email" type="email"/>
<label>FirstName</label>
<input firstName="firstName" type="text"/>
<label>LastName</label>
<input lastName="lastName" type="text"/>
<label>Password</label>
<input password="password" type="password"/>
<input type="submit" value="submit" />
</form>
</div>
<div class="col-md-6">dd</div>
</div>
</div>
</div>
{{template "footer"}}
{{end}}
Посылаю форму на роут
package routes
import (
"Avangard/controller"
"github.com/labstack/echo/v4"
)
func GetUserApiRoutes(e *echo.Echo, userController *controller.UserController) {
v1 := e.Group("/api/v1")
{
v1.POST("/login", userController.AuthenticateUser)
v1.GET("/users", userController.GetAllUser)
v1.POST("/signup", userController.SaveUser)
v1.GET("/users/:id", userController.GetUser)
v1.PUT("/users/:id", userController.UpdateUser)
v1.DELETE("/users/:id", userController.DeleteUser)
}
}
После чего обрабатываю в контроллере
package controller
import (
"Avangard/exception"
model "Avangard/models"
"Avangard/repository"
"Avangard/security"
"Avangard/util"
"log"
"net/http"
"strconv"
"github.com/labstack/echo/v4"
)
type UserController struct {
userRepository repository.UserRepository
authValidator *security.AuthValidator
}
func NewUserController(userRepository repository.UserRepository, authValidator *security.AuthValidator) *UserController {
return &UserController{userRepository: userRepository, authValidator: authValidator}
}
func (userController *UserController) SaveUser(c echo.Context) error {
payload := new(model.UserInput)
//email := c.FormValue("email")
log.Println(c, payload)
if err := util.BindAndValidate(c, payload); err != nil {
return err
}
_, err := userController.userRepository.FindByEmail(payload.Email)
if err == nil {
return exception.ConflictException("User", "email", payload.Email)
}
user := &model.User{UserInput: payload}
//encrypt password
err = beforeSave(user)
if err != nil {
return err
}
createdUser, err := userController.userRepository.SaveUser(user)
if err != nil {
return err
}
return util.Negotiate(c, http.StatusCreated, createdUser)
}
func beforeSave(user *model.User) (err error) {
hashedPassword, err := util.EncryptPassword(user.Password)
if err != nil {
return err
}
user.Password = string(hashedPassword)
return nil
}
После какая то магия в репозитории (я так и не понял как это работает, разбираюсь)
package repository
import (
"Avangard/exception"
model "Avangard/models"
"context"
paginate "github.com/gobeam/mongo-go-pagination"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
var cntx context.Context = context.TODO()
type UserRepository interface {
GetAllUser(page int64, limit int64) (*model.PagedUser, error)
SaveUser(user *model.User) (*model.User, error)
FindByEmail(email string) (*model.User, error)
GetUser(id string) (*model.User, error)
UpdateUser(id string, user *model.User) (*model.User, error)
DeleteUser(id string) error
}
type userRepositoryImpl struct {
Connection *mongo.Database
}
func (userRepository *userRepositoryImpl) FindByEmail(email string) (*model.User, error) {
var existingUser model.User
filter := bson.M{"email": email}
err := userRepository.Connection.Collection("users").FindOne(cntx, filter).Decode(&existingUser)
if err != nil {
return nil, err
}
return &existingUser, nil
}
func NewUserRepository(Connection *mongo.Database) UserRepository {
return &userRepositoryImpl{Connection: Connection}
}
func (userRepository *userRepositoryImpl) GetAllUser(page int64, limit int64) (*model.PagedUser, error) {
var users []model.User
filter := bson.M{}
collection := userRepository.Connection.Collection("users")
// projection := bson.D{
// {"id", 1},
// {"firstName", 1},
// {"lastName", 1},
// {"email", 1},
// }
//
projection := bson.D{
{
Key: "id",
Value: 1,
},
{
Key: "firstName",
Value: 1,
}, {
Key: "lastName",
Value: 1,
},
{
Key: "email",
Value: 1,
}}
paginatedData, err := paginate.New(collection).Context(cntx).Limit(limit).Page(page).Select(projection).Filter(filter).Decode(&users).Find()
if err != nil {
return nil, err
}
return &model.PagedUser{
Data: users,
PageInfo: paginatedData.Pagination,
}, nil
}
func (userRepository *userRepositoryImpl) SaveUser(user *model.User) (*model.User, error) {
user.ID = primitive.NewObjectID()
_, err := userRepository.Connection.Collection("users").InsertOne(cntx, user)
if err != nil {
return nil, err
}
user.Password = ""
return user, nil
}
func (userRepository *userRepositoryImpl) GetUser(id string) (*model.User, error) {
var existingUser model.User
objectId, _ := primitive.ObjectIDFromHex(id)
filter := bson.M{"_id": objectId}
err := userRepository.Connection.Collection("users").FindOne(cntx, filter).Decode(&existingUser)
if err != nil {
return nil, exception.ResourceNotFoundException("User", "id", id)
}
existingUser.Password = ""
return &existingUser, nil
}
func (userRepository *userRepositoryImpl) UpdateUser(id string, user *model.User) (*model.User, error) {
objectId, _ := primitive.ObjectIDFromHex(id)
filter := bson.M{"_id": objectId}
result, err := userRepository.Connection.Collection("users").ReplaceOne(cntx, filter, user)
if err != nil {
return nil, err
}
if result.MatchedCount == 0 {
return nil, exception.ResourceNotFoundException("User", "id", id)
}
user.ID = objectId
user.Password = ""
return user, nil
}
func (userRepository *userRepositoryImpl) DeleteUser(id string) error {
objectId, _ := primitive.ObjectIDFromHex(id)
filter := bson.M{"_id": objectId}
result, err := userRepository.Connection.Collection("users").DeleteOne(cntx, filter)
if err != nil {
return err
}
if result.DeletedCount == 0 {
return exception.ResourceNotFoundException("User", "id", id)
}
return nil
}
Эта магия тоже не до конца понятна, как обрабатывать form-data и что именно возвращать
package util
import (
"github.com/labstack/echo/v4"
)
func Negotiate(c echo.Context, code int, i interface{}) error {
mediaType := c.QueryParam("mediaType")
switch mediaType {
case "xml":
return c.XML(code, i)
case "json":
return c.JSON(code, i)
//case "multipart/form-data":
// return
default:
return c.JSON(code, i)
}
}
Если с Postman указать Content-Type application/json и отправить данные то все нормально работает, но если отправлять с формы multipart/form-data то просто не проходит валидация, пишет что валидация не прошла {"time":"2021-05-22T21:45:31.134641998+05:00","level":"ERROR","prefix":"echo","file":"error.go","line":"53","message":"code=400, message=Key: 'UserInput.FirstName' Error:Field validation for 'FirstName' failed on the 'required' tag\nKey: 'UserInput.LastName' Error:Field validation for 'LastName' failed on the 'required' tag\nKey: 'UserInput.Email' Error:Field validation for 'Email' failed on the 'required' tag\nKey: 'UserInput.Password' Error:Field validation for 'Password' failed on the 'required' tag"}
Помогите пожалуйста!