I want to see the value of a variable in the interface function, I tried fmt, logging, but I can't see the output of this variable when I run the service
// #Router /job/batchadd [post]
func HandleBatchAdd(c *gin.Context) {
var req reqBatchCreate
var resp respBatchCreate
err := c.ShouldBindJSON(&req)
if err != nil {
helper.ReplyErr(c, errcode.ErrInvalidParam, err)
return
}
...
resp.JobIDs = ids
logging.Default().Info(" HandleBatchAdd api ids= ", ids)
helper.Ok(c, resp)
return
}
For example, In this code , I don't know where I can see the output information , logging Default().Info and helperReplyErr
Related
Im using postman to post data and in the body Im putting some simple json
Request Body
{
"order":"1",
"Name":"ts1"
}
I need to transfer the data to json and I try like following,
and I wasnt able to get json, any idea what is missing
router.POST("/user", func(c *gin.Context) {
var f interface{}
//value, _ := c.Request.GetBody()
//fmt.Print(value)
err2 := c.ShouldBindJSON(&f)
if err2 == nil {
err = client.Set("id", f, 0).Err()
if err != nil {
panic(err)
}
}
The f is not a json and Im getting an error, any idea how to make it work?
The error is:
redis: can't marshal map[string]interface {} (implement encoding.BinaryMarshaler)
I use https://github.com/go-redis/redis#quickstart
If I remove the the body and use hard-coded code like this I was able to set the data, it works
json, err := json.Marshal(Orders{
order: "1",
Name: "tst",
})
client.Set("id", json, 0).Err()
If you only want to pass the request body JSON to Redis as a value, then you do not need to bind the JSON to a value. Read the raw JSON from the request body directly and just pass it through:
jsonData, err := ioutil.ReadAll(c.Request.Body)
if err != nil {
// Handle error
}
err = client.Set("id", jsonData, 0).Err()
let's do it with an example. Assume your request body has a user email like this:
{ email: "test#test.com" }
and now you want to get this email on the back-end. first, define a struct like the following:
type EmailRequestBody struct {
Email string
}
Now you can easily bind the email value in your request body to the struct you defined: first, define a variable for your struct and then bind the value:
func ExampleFunction(c *gin.Context) {
var requestBody EmailRequestBody
if err := c.BindJSON(&requestBody); err != nil {
// DO SOMETHING WITH THE ERROR
}
fmt.Println(requestBody.Email)
}
you can easily access the email value and print it out or do whatever you need :
fmt.Println(requestBody.Email)
Or you can use GetRawData() function as:
jsonData, err := c.GetRawData()
if err != nil{
//Handle Error
}
err = client.Set("id", jsonData, 0).Err()
If you want to get json body like other frameworks like express(Nodejs), you can do the following
bodyAsByteArray, _ := ioutil.ReadAll(c.Request.Body)
jsonBody := string(bodyAsByteArray)
func Login(c echo.Context) error {
user := &users.User{}
if err := c.Bind(&user); err != nil {
return err
}
return token.SigIn(c, user.Email, user.Password)
}
This is my Login function that retrieve the token when the user send the requests.
the Signin func that handle the token
func SigIn(c echo.Context, email, password string) error {
user := users.User{}
db := database.SetUp()
if err := db.Where("email = ?", email).First(&user).Error; gorm.IsRecordNotFoundError(err) {
restErr := errors.NewBadRequestError("Invalid credentials")
return c.JSON(http.StatusBadRequest, restErr)
}
if user.VerifyPassword(password) != nil {
restErr := errors.NewUnauthorizedError("Couldn't log you in with these credentials")
return c.JSON(http.StatusUnauthorized, restErr)
}
//user is successfull
return CreateToken(c)
}
the CreateToken func is as follow
type TokenJWT struct {
Token string `json:"token"`
}
func CreateToken(c echo.Context) error {
token := jwt.New(jwt.SigningMethodHS256)
claims := token.Claims.(jwt.MapClaims)
claims["authorized"] = true
claims["name"] = "Pascal Gaetan"
claims["exp"] = time.Now().Add(time.Hour * 1).Unix()
// Generate encoded token and send it as response.
t, err := token.SignedString([]byte("my_secret_key"))
if err != nil {
return err
}
return c.JSON(http.StatusOK, TokenJWT{
Token: t,
})
}
when everyhting is succesfull, i would like to get the authenticated user through an URL /api/me that calls a Me function
Let me split your question into two parts: the first one is how to easily encode and decode user in or from JWT token and the second part is how to write a generic code which can retrieve user from everywhere.
From your example I mentioned that you created a MapClaims but to reduce parsing complexity it will be better to create a token using a custom claims type. If you are using dgrijalva/jwt-go, then according to documentation you can do something like that
type UserClaims struct {
Name string `json:"name"`
jwt.StandardClaims
}
// encode it as before, but with your created type
t := jwt.New(signer)
userClaims := &UserClaims{Name: "Burmese"}
t.Claims = userClaims
tokenString, err = t.SignedString(]byte("my_secret_key"))
then you can parse your user in your router/framework middleware with
tokenString := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJmb28iOiJiYXIiLCJleHAiOjE1MDAwLCJpc3MiOiJ0ZXN0In0.HE7fK0xOQwFEr4WDgRWj4teRPZ6i3GLwD5YCm6Pwu_c"
token, err := jwt.ParseWithClaims(tokenString, &UserClaims{}, func(token *jwt.Token) (interface{}, error) {
return []byte("my_secret_key"), nil
})
if claims, ok := token.Claims.(*UserClaims); ok && token.Valid {
fmt.Printf("%v %v", claims.Name, claims.StandardClaims.ExpiresAt)
} else {
fmt.Println(err)
}
This example was adopted from an official documentation here
Now you know how to parse authenticated user struct with ease and the next logic move is to wrap it into your middleware. Whether there are a lot of implementation details like you can retrieve JWT from cookie, header or query, also defining some ordering on them, the gist the following: you should have wrapped abovementioned code into your middleware and after parsing the struct you can pass it via your request context. I don't use echo and other frameworks, but for pure net/http you can pass your parsed struct from middleware with
context.WithValue(ctx, UserCtxKey, claims)
Hope it helps!
This is a fairly common design pattern to create an authenticated client and then call various action methods on it. You could do something like the following:
type Client struct {
... // other members
token string // unexported unless there is a special reason to do otherwise
}
func NewClient(c echo.Context, email, password string) (*Client, error) {
user := users.User{}
cl := Client{}
... // your original method
cl.token = token
return &cl, nil
}
func (c *Client) DoSomething(...) ... { ... }
Using Go and AWS-SDK
I'm attempting to query route53 CNAME and A records as listed in the AWS Console under Route53 -> Hosted Zones. I'm able to query using the following code, but it requires the (cryptic) HostedZoneId I have to know ahead of time.
Is there a different function, or a HostedZoneId lookup based on the Domain Name such as XXX.XXX.com ?
AWSLogin(instance)
svc := route53.New(instance.AWSSession)
listParams := &route53.ListResourceRecordSetsInput{
HostedZoneId: aws.String("Z2798GPJN9CUFJ"), // Required
// StartRecordType: aws.String("CNAME"),
}
respList, err := svc.ListResourceRecordSets(listParams)
if err != nil {
fmt.Println(err.Error())
return
}
// Pretty-print the response data.
fmt.Println("All records:")
fmt.Println(respList)
edit: oh, additionally, the StartRecordType with the value "CNAME" throws a validation error, so I'm not sure what I should be using there.
You first have to do a lookup to get the HostedZoneID. Here is the func I wrote for it. :
func GetHostedZoneIdByNameLookup(awsSession string, HostedZoneName string) (HostedZoneID string, err error) {
svc := route53.New(awsSession)
listParams := &route53.ListHostedZonesByNameInput{
DNSName: aws.String(HostedZoneName), // Required
}
req, resp := svc.ListHostedZonesByNameRequest(listParams)
err = req.Send()
if err != nil {
return "", err
}
HostedZoneID = *resp.HostedZones[0].Id
// remove the /hostedzone/ path if it's there
if strings.HasPrefix(HostedZoneID, "/hostedzone/") {
HostedZoneID = strings.TrimPrefix(HostedZoneID, "/hostedzone/")
}
return HostedZoneID, nil
}
I'm a bit new to Go and I'm having trouble converting a response object from an API call into different structures based on the request type
Essentially, I have one func which sends out a request
func (fpc *FPClient) request(path string, method string, params interface{}, Token string, response interface{}) *dto.AppError {
client := &http.Client{
Timeout: time.Second * 15,
}
requestBody, err := json.Marshal(params)
if err != nil {
//
}
req, _ := http.NewRequest(method, path, bytes.NewBuffer(requestBody))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("Cookie", fmt.Sprintf("cookie=%s;", Token))
req.SetBasicAuth(fpc.username, fpc.password)
resp, err := client.Do(req)
if err != nil {
//
}
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
//
}
if FPErr := fpc.processErrors(resp, body); FPerr != nil {
return FPErr
}
responseData := FPApiSuccess{Head: response, Body: response}
if err := json.Unmarshal(body, &responseData); err != nil {
//
}
fmt.Printf("\n\n client Response : \n %+v \n", responseData.Body)
return nil
}
The struct for FPApiSuccess is:
type FPApiSuccess struct {
Body interface{} `json:"body"`
Head interface{} `json:"head"`
}
Right now, there are 2 calling functions and the API response expected is a bit different for both
Both API responses have the below structure
{
"head": {},
"body": {}
}
However, the nested details in each key is different based on the API used.
I want to capture the head and body keys in a struct argument I give and send it back to the calling function. The response argument in the request function is a different struct type based on the calling function.
I'm unable to get this to work - I'm only getting back a blank struct from the request function. This is the fmt.PrintF log
client Response :
&{Body:{BOrderID:0 CC: Exch: EOID: Type: Local:0 Message: } Head:{ResponseCode: Status: StatusDescription:}}
This is an empty struct - ideally, it should be populated with the values retrieved from the API.
For reference, heres the struct passed as an argument as response in the request function:
type FPApiResponse struct {
Body FPBodyResponse `json:"body"`
Head FPHeadResponse `json:"head"`
}
type FPHeadResponse struct {
ResponseCode string `json:"responseCode"`
Status string `json:"status"`
StatusDescription string `json:"statusDescription"`
}
type FPBodyResponse struct {
BOrderID int `json:"BOrderID"`
CC string `json:"CC"`
Exch string `json:"Exch"`
EOID string `json:"EOID"`
Type string `json:"Type"`
Local int `json:"Local"`
Message string `json:"Message"`
}
Update
So I did this; instead of
responseData := FPApiSuccess{Head: response, Body: response}
I did this
responseData := fivePaisaApiSuccess{}
So now, I get the below in console
Client Response :
{Body:map[BOrderID:0 CC:52715111 Type:D Local:0 Message:Invalid Session ] Head:map[responseCode:5POrdReq status:0 statusDescription:Success]}
So essentially, this works, but the calling function doesn't seem to get the proper response:
Here's the calling function
func (fpc *FPClient) PlaceOrder(orderParams dto.OrderBodyParams, variety string, Token string) (string, *dto.AppError) {
var result FPApiResponse
headParams := dto.FFPOrderHeadParams{
//
}
FPOrderParams := dto.FPOrderParams{
//
}
FPErr := fpc.request(FPURL+FPPlaceOrder, http.MethodPost, FPOrderParams, brokerAccessToken, &result)
if FPErr != nil {
return "", FPErr
}
fmt.Printf("\n\n Client result : \n %+v \n", result)
if result.Head.Status != "0" {
//
}
if result.Body.Status != 0 {
//
}
return strconv.Itoa(result.Body.Broker), nil
}
The result value is blank:
{Body:{BOId:0 CC: Exch: Type: Local:0 Message: Status:0} Head:{ResponseCode: Status: StatusDescription:}}
I don't understand, this pointer is getting populated in the request function
Here's the struct I'm passing to the request:
type FPApiResponse struct {
Body FPBodyResponse `json:"body"`
Head FPHeadResponse `json:"head"`
}
I am facing an issue where i have made an api in Go every thing work fine but i am not getting data in postman. When i print the data in logs i am getting the data properly but it is showing blank data in postman.
authorizeModel.go
func GetSkillList() map[string]interface{} {
db := GetDB()
var (
// id int
skillName string
)
type SkillList struct {
name string
}
skillList := SkillList{}
skillArr := []SkillList{}
rows, err := db.Query("select DISTINCT(name) as name from skills where company_id IN ('2') and name != 'Skill Needed' order by name")
if err != nil {
log.Fatal(err)
}
defer rows.Close()
for rows.Next() {
err := rows.Scan(&skillName)
if err != nil {
log.Fatal(err)
}
skillList.name = skillName
skillArr = append(skillArr, skillList)
}
response := u.Message(true, "Skill list retrieved successfully")
response["data"] = skillArr
log.Println(skillArr)
response["authorization"] = false
return response
}
authController.go
var SkillTagList = func(w http.ResponseWriter, r *http.Request) {
resp := models.GetSkillList()
u.Respond(w, resp)
}
routes.go
router.HandleFunc("/api/v1/authorize/skillTagList", controllers.SkillTagList).Methods("POST")
If you see authorizeModel.go i have printed my data in logs i am getting that data successfully in logs. But see the postman screenshot below.
You have to rename name to Name
I'm not sure what is u.Respond(), so I will assume it's a helper function of some framework that you are using, and I will assume u.Respond() is internally using json.Marshal.
If your struct has unexported fields(fields name starting with lowercase letter, in your case name), json.Marshal cannot access those field, and the result won't have name field. That is why you are getting empty objects in JSON.