Gin framework can not get the data from Postman,below is a demo:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
)
type CreateRequest struct {
Username string `json:"username"`
Phone string `json:"phone"`
Password string `json:"password"`
}
func Signup(c *gin.Context) {
var r CreateRequest
if err := c.Bind(&r); err != nil {
fmt.Println(err)
}
fmt.Println("this is debug info:start")
fmt.Println(r.Username)
fmt.Println("this is debug info:end")
}
func main() {
r := gin.Default()
r.POST("/signup", Signup)
r.Run() // listen and serve on 0.0.0.0:8080
}
Send a request from postman:
Debug info:
Where is the problem?
What should I do?
Accordingly to your screen it looks like you're sending x-www-form-urlencoded request and it this case you have to have form tags in your CreateRequest struct:
type CreateRequest struct {
Username string `json:"username" form:"username"`
Phone string `json:"phone" form:"phone"`
Password string `json:"password" form:"password"`
}
You should use form:"username" in order to POST using URI. Unless you want to POST it using body JSON, then you can only set json:"username" in your struct.
Related
I am trying to create a common HTTP request validator middleware function that accepts type (maybe reflect.Type) as an argument and then using the package github.com/go-playground/validator/v10 to be able to unmarshall JSON into struct of mentioned type and validate the struct. I've tried to explain with the following example code...
EXAMPLE
type LoginRequestBody struct {
Username string `json:"username",validate:"required"`
Password string `json:"password",validate:"required"`
}
type SignupReqBody struct {
Username string `json:"username",validate:"required"`
Password string `json:"password",validate:"required"`
Age int `json:"age",validate:"required"`
}
// sample routers with a common middleware validator function
router.POST("/login", ReqValidate("LoginRequestBody"), LoginController)
router.POST("/signup", ReqValidate("SignupReqBody"), SignupController)
func ReqValidate(<something>) gin.HandlerFunc {
return func (c *gin.Context) {
// unmarshalling JSON into a struct
// common validation logic...
c.Next()
}
}
Overall, i wanna achieve the same validator flexibility as there in Node.js using Joi package.
I don't know if it is necessary to use middleware but I was recently trying to do something and I found an excellent tutorial that you can see here.
With Gin You can use binding:
Example:
package main
import (
"github.com/gin-gonic/gin"
"net/http"
)
type AnyStruct struct {
Price uint `json:"price" binding:"required,gte=10,lte=1000"`
}
func main() {
engine:=gin.New()
engine.POST("/test", func(context *gin.Context) {
body:=AnyStruct{}
if err:=context.ShouldBindJSON(&body);err!=nil{
context.AbortWithStatusJSON(http.StatusBadRequest,
gin.H{
"error": "VALIDATEERR-1",
"message": "Invalid inputs. Please check your inputs"})
return
}
context.JSON(http.StatusAccepted,&body)
})
engine.Run(":3000")
}
Don't use commas to separate struct tag key-value pairs, use space.
You can use generics (type parameters) to replace <something> but your controllers need to have the concrete type as their argument.
For example:
func ReqValidate[T any](next func(*gin.Context, *T)) gin.HandlerFunc {
return func(c *gin.Context) {
params := new(T)
if err := c.ShouldBindJSON(params); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
next(c, params)
}
}
And then the controllers:
type LoginRequestBody struct {
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
}
func LoginController(c *gin.Context, params *LoginRequestBody) {
// ...
}
type SignupReqBody struct {
Username string `json:"username" validate:"required"`
Password string `json:"password" validate:"required"`
Age int `json:"age" validate:"required"`
}
func SignupController(c *gin.Context, params *SignupReqBody) {
// ...
}
And then the routing:
router := gin.Default()
router.POST("/login", ReqValidate(LoginController))
router.POST("/signup", ReqValidate(SignupController))
I'm using the gin framework and the following code works fine for me
import (
"github.com/gin-gonic/gin"
"net/http"
)
type RequestBody struct {
MyRequiredField string `json:"myRequiredField" binding:"required"`
}
func Handle(context *gin.Context) {
var requestBody RequestBody
err := context.ShouldBindJSON(&requestBody)
if err != nil {
context.JSON(http.StatusBadRequest, err.Error())
return
}
// ...
}
I would like to know how I can tell gin that the given struct must be the request body. It must not search for fields in the route parameters or queries.
So is there a way to be more explicit, e.g.
err := context.BindStructToRequestBody(&requestBody)
I am trying to write simple http request to get data from Zendesk API. The simple request works, but I am not able to find out any example to handle pagination. Unfortunatelly, I was not able able find any example on the net related to consuming REST API with pagination.
I am new to Go and I am not professional programmer. I have the code working in Python where I use while loop with NextPage variable. But I am trying to learn Go and trying to rewrite it.
Please can you give me an advise? Should I do it in the same way using a loop with NextPage and appending json data, or is there any other way?
Please assume I am learning. Thank you.
package main
import (
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strconv"
"time"
)
type Response struct {
Organizations []struct {
URL string `json:"url"`
ID int64 `json:"id"`
Name string `json:"name"`
SharedTickets bool `json:"shared_tickets"`
SharedComments bool `json:"shared_comments"`
ExternalID interface{} `json:"external_id"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
DomainNames []string `json:"domain_names"`
Details interface{} `json:"details"`
Notes interface{} `json:"notes"`
GroupID interface{} `json:"group_id"`
Tags []interface{} `json:"tags"`
OrganizationFields struct {
SevenX24HourSupportPurchased interface{} `json:"7x24_hour_support_purchased_"`
K8SNodesLicensed interface{} `json:"k8s_nodes_licensed"`
LatestClosedOpportunityDate interface{} `json:"latest_closed_opportunity_date"`
PartnerOfRecordInSf string `json:"partner_of_record_in_sf"`
VcpusLicensed interface{} `json:"vcpus_licensed"`
} `json:"organization_fields"`
} `json:"organizations"`
NextPage string `json:"next_page"`
PreviousPage interface{} `json:"previous_page"`
Count int `json:"count"`
}
func organizationsGet() {
url := "https://zendesk.zendesk.com/api/v2/organizations.json"
req, _ := http.NewRequest("GET", url, nil)
req.Header.Add("Authorization", "Basic blabla")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
fmt.Println(res)
fmt.Println(string(body))
var responseObject Response
json.Unmarshal(body, &responseObject)
for i := 0; i < len(responseObject.Organizations); i++ {
fmt.Println(responseObject.Organizations[i].Name + " " + strconv.FormatInt(int64(responseObject.Organizations[i].ID), 10))
// fmt.Printf("%v,%v\n", responseObject.User[i].Name, responseObject.User[i].Email)
}
}
I'm nob in GO :) just try to create simple crud throw it using gin and plugin called ozzo-validation
My code:
package models
import (
validation "github.com/go-ozzo/ozzo-validation"
"gorm.io/gorm"
)
type Post struct {
gorm.Model
Name string `gorm:"type:varchar(255);"`
Desc string `gorm:"type:varchar(500);"`
}
type PostData struct {
Name string `json:"name"`
Desc string `json:"desc"`
}
func (p PostData) Validate() error {
return validation.ValidateStruct(&p,
validation.Field(&p.Name, validation.Required, validation.Length(5, 20)),
validation.Field(&p.Desc, validation.Required),
)
}
PostController:
package controllers
import (
"curd/app/models"
"fmt"
"github.com/gin-gonic/gin"
)
func Store(c *gin.Context) {
// Validate Input
var post models.PostData
err := post.Validate()
fmt.Println(err)
}
{
"name": "sdfsdfsfsdf"
}
The problem is once I submit the above JSON data from the postman the validation gives me this in terminal :
desc: cannot be blank; name: cannot be blank.
As noted in the comments, you need to decode the data from the HTTP request into the struct before you can perform validation. The validation errors you're seeing are a product of calling Validate() on a fresh instance (with zero values in every field) of your Post struct. Try this.
func Store(c *gin.Context) {
var post models.PostData
// This will infer what binder to use depending on the content-type header.
if err := c.ShouldBind(&post); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
// Validate Input
err := post.Validate()
fmt.Println(err)
}
It would be awesome to have a straight forward mapping from the standard library URL.Query() to an struct.
Query() returns a map like:
map[a:[aaaa] b:[bbbb] c:[cccc]]
The struct looks like:
type Thing struct {
A string
B string
C string
}
I've no idea why URL.Query returns a map with array elements inside tough. (well.. I know why but a GET is not likely to have duplicated params)
Please find below the complete example of parsing get query params directly in a golang struct and then sending the struct back as response
package main
import (
"log"
"net/http"
"encoding/json"
"github.com/gorilla/schema"
)
var decoder = schema.NewDecoder()
type EmployeeStruct struct {
MemberId string `schema:"memberId"`
ActivityType string `schema:"activityType"`
BusinessUnitCode int `schema:"businessUnitCode"`
}
func GetEmployee(w http.ResponseWriter, r *http.Request) {
var employeeStruct EmployeeStruct
err := decoder.Decode(&employeeStruct, r.URL.Query())
if err != nil {
log.Println("Error in GET parameters : ", err)
} else {
log.Println("GET parameters : ", employeeStruct)
}
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(employeeStruct)
}
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/GetEmployee", GetEmployee)
log.Fatal(http.ListenAndServe(":8080", mux))
}
Steps to execute & Test (Assuming you are saving above code in employee.go) :
Step 1 : go run employee.go
Step 2 : Open in browser http://localhost:8080/GetEmployee?memberId=123&activityType=Call&businessUnitCode=56
Step 3 : You should get below response in browser window
{
"MemberId": "123",
"ActivityType": "Call",
"BusinessUnitCode": 56
}
Step 4 : On console you should see below
GET parameters : {123 Call 56}
example:
filters={"reference":["docker.io/library/alpine:latest"]}
need url encode to:
filters=%7B%22reference%22%3A%5B%22docker.io%2Flibrary%2Falpine%3Alatest%22%5D%7D
and could use "github.com/gorilla/schema"
query := struct {
All bool
Filters map[string][]string `schema:"filters"`
Digests bool
Filter string
}{}
decoder := schema.NewDecoder()
decoder.Decode(&query, r.URL.Query())
As pointed out by #mh-cbon gorilla schema is the ultimate solution here.
Instead for obtaining the queryParams from the URL attribute.
func handleRequest(w http.ResponseWriter, r *http.Request) {
queryString := r.URL.Query()
//...parsing the Values -> map[string][]string
}
The approach of gorilla schema is to ship r.PostForm to the decode function.
func handleRequest(w http.ResponseWriter, r *http.Request) {
err := decoder.Decode(person, r.PostForm)
//...using reflect each struct's property can be called using
// the PostForm(url string, data url.Values) signature
fmt.Print(person.GoodJobGorilla)
}
Just parse the string to URL and after you can use the lib github.com/gorilla/schema to parse it :)
// Example to parse querystring to struct
package main
import (
"log"
"net/url"
"github.com/gorilla/schema"
)
type URLParams struct {
Code string `schema:"code"`
State string `schema:"state"`
}
func main() {
var (
params URLParams
decoder = schema.NewDecoder()
)
p := "https://www.redirect-url.com?code=CODE&state=RANDOM_ID"
u, _ := url.Parse(p)
err := decoder.Decode(¶ms, u.Query())
if err != nil {
log.Println("Error in Decode parameters : ", err)
} else {
log.Printf("Decoded parameters : %#v\n", params)
}
}
https://go.dev/play/p/CmuPhdKh6Yg
Using ggicci/httpin
Disclaimer: I'm the creator and maintainer of this package.
httpin helps you easily decoding HTTP request data from
Query parameters, e.g. ?name=john&is_member=true
Headers, e.g. Authorization: xxx
Form data, e.g. username=john&password=******
JSON/XML Body, e.g. POST {"name":"john"}
Path variables, e.g. /users/{username}
File uploads
How to use?
type ListUsersInput struct {
Page int `in:"query=page"`
PerPage int `in:"query=per_page"`
IsMember bool `in:"query=is_member"`
}
func ListUsers(rw http.ResponseWriter, r *http.Request) {
input := r.Context().Value(httpin.Input).(*ListUsersInput)
if input.IsMember {
// Do sth.
}
// Do sth.
}
httpin is:
well documented: at https://ggicci.github.io/httpin/
well tested: coverage over 98%
open integrated: with net/http, go-chi/chi, gorilla/mux, gin-gonic/gin, etc.
extensible (advanced feature): by adding your custom directives. Read httpin - custom directives for more details.
awesome mentioned: https://github.com/avelino/awesome-go#forms
You can use the graceful package of Echo.
I write some codes as an example, with self-explanatory comments
package main
import (
"log"
"github.com/labstacks/echo"
)
// Declare your struct with form: "" tag
type Employee struct {
MemberId string `form:"memberId"`
ActivityType string `form:"activityType"`
BusinessUnitCode int `form:"businessUnitCode"`
}
// Your handlers should look like this method
// Which takes an echo.Context and returns an error
func GetEmployee(ctx echo.Context) error{
var employee Employee
// With Bind, you can get the Post Body or query params from http.Request
// that is wrapped by echo.Context here
if err := ctx.Bind(&employee);err != nil {
return err
}
// now you can use your struct , e.g
return ctx.json(200, employee.MemberId)
}
// now use the handler in your main function or anywhere you need
func main() {
e := echo.New()
e.Get("/employee", GetEmployee)
log.Fatal(e.Start(":8080"))
}
If anyone is using Echo, query struct tag will be useful for this case.
Example Struct
type struct Name {
FirstName string `query:"first_name"`
LastName string `query:"last_name"`
}
Example Query Param
?first_name="shahriar"&last_name="sazid"
Code
var name Name
err := c.Bind(&name); if err != nil {
return c.String(http.StatusBadRequest, "bad request")
}