I have a function named LoginUser(c *gin.Context) with argument c *gin.Context. I want to call LoginUser from another function CreateBlogsWithUser. But LoginUser requires username and password. I tried to pass in c.Request.Body but it is not working.
func (server *Server) CreateBlogsWithUser() {
resp := httptest.NewRecorder()
gin.SetMode(gin.TestMode)
c, _ := gin.CreateTestContext(resp)
c.Request.Header.Add("Content-Type","application/json")
c.Request.Body.Add("uname","test") //this line is not working
c.Request.Body.Add("password","test#123") //this line is not working
LoginUser(c)
}
func LoginUser(c *gin.Context) {
requestData := models.CCPADefaultData{}
err := json.NewDecoder(c.Request.Body).Decode(&requestData)
if err != nil {
errList["Invalid_body"] = "Missing request parameters."
c.JSON(http.StatusUnprocessableEntity, gin.H{
"status": http.StatusUnprocessableEntity,
"error": "Always come in this if condition.",
})
return
}
//XXXXXXXXX
//Code
}
I tried many things but none is working for me.
Please help how to pass parameter if argument is c *gin.Context
Related
i receive a response body "bad request" with "httptest.Client().Postform"
type testServer struct {
*httptest.Server
}
func newTestServer(t *testing.T, h http.Handler) *testServer {
ts := httptest.NewTLSServer(h)
jar, err := cookiejar.New(nil)
if err != nil {
t.Fatal(err)
}
ts.Client().Jar = jar
ts.Client().CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
return &testServer{ts}
}
func (ts *testServer) postForm(t *testing.T, urlPath string, form url.Values) (int, http.Header, string) {
rs, err := ts.Client().PostForm(ts.URL+urlPath, form)
if err != nil {
t.Fatal(err)
}
defer rs.Body.Close()
body, err := io.ReadAll(rs.Body)
if err != nil {
t.Fatal(err)
}
bytes.TrimSpace(body)
return rs.StatusCode, rs.Header, string(body)
}
I don't know where is the problem, i have also verified the url it's correct.
Always badrequest with POST but with GET request it's works fine.
this is the handler object :
func (app *application) routes() http.Handler {
router := httprouter.New()
router.NotFound = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
app.notFound(w)
})
dynamic := alice.New(app.sessionManager.LoadAndSave, noSurf, app.Authenticated)
router.Handler(http.MethodGet, "/", dynamic.ThenFunc(app.home))
router.Handler(http.MethodGet, "/user/signup", dynamic.ThenFunc(app.userSignup))
router.Handler(http.MethodPost, "/user/signup", dynamic.ThenFunc(app.userSignupPost))
standart := alice.New(app.recoverPanic, app.logRequest, securityHeaders)
return standart.Then(router)
}
the test function :https://go.dev/play/p/k45-JYTYCOS
the app.userSignupPost:
func (app *application) userSignupPost(w http.ResponseWriter, r *http.Request) {
var form userSignupForm
err := app.decodPostForm(r, &form)
if err != nil {
app.clientError(w, http.StatusBadRequest)
return
}
form.CheckField(validator.NotBlank(form.Name), "name", "this field must not be blank")
form.CheckField(validator.NotBlank(form.Email), "email", "this field must not be blank")
form.CheckField(validator.Matches(form.Email, validator.EmailRX), "email", "this field must be a valid email address")
form.CheckField(validator.NotBlank(form.Password), "password", "this field must not be blank")
form.CheckField(validator.MinChars(form.Password, 8), "password", "password must bee at least 8 caracter long")
if !form.Valid() {
data := app.newTemplateData(r)
data.Form = form
app.render(w, http.StatusUnprocessableEntity, "signup.tmpl.html", data)
return
}
err = app.users.Insert(form.Name, form.Email, form.Password)
if err != nil {
if errors.Is(err, models.ErrDuplicateEmail) {
form.AddFieldError("email", "Email already exist")
data := app.newTemplateData(r)
data.Form = form
app.render(w, http.StatusUnprocessableEntity, "signup.tmpl.html", data)
} else {
fmt.Println("error user postform")
app.serverError(w, err)
}
return
}
app.sessionManager.Put(r.Context(), "flash", "Signup Successful. Please log in")
http.Redirect(w, r, "/user/login", http.StatusSeeOther)
}
It appears that you're using https://github.com/justinas/alice to register handlers - you don't want to do this. That package is for middleware chaining - e.g. "before all requests to this URL, first authenticate the request" - you'd put the authentication into a middleware function and then add it to the chain.
So every POST /user/signup request is getting passed first to app.userSignup() (what you are using to handle GET requests). This is because calling alice.ThenFunc() appends the passed handler to the chain and then returns the entire chain as a handler - you need to read this part of the Alice docs carefully before using it.
Replace this line:
router.Handler(http.MethodPost, "/user/signup", dynamic.ThenFunc(app.userSignupPost))
with
router.Handler(http.MethodPost, "/user/signup", http.HandlerFunc(app.userSignupPost))
You may not need the additional decoration of http.HandlerFunc() - try it with and without to see what works. I cannot say for sure without knowing what the body of app.userSignupPost() looks like (same for the other handler functions as well).
You'll then need to do the same for the other handler registration lines - you shouldn't be using middleware chaining for your end handlers. An http.Handler is used for saying, "send any request to path /PP/ppp with method XXXX to this function." Middleware chaining is for preprocessing (authentication, authorization, etc.) - a whole host of things can be done there, but end request handling shouldn't be one of them.
I'm still curious if your use of PostForm() is going to cause you issues for the reason I cited in my comment on your question - try a raw Post() and see if the behavior differs, but after refactoring to take out the alice goop (at least temporarily). When testing a handler, I'd start off with a much more minimal approach - test that the handler itself works before muddying the waters with both alice and what looks like this package.
I think i found the problem , the session cookie are not the same for get and post request. i don't know why it has changed.They use the same http.Client()
I have problem in Golang echo framwork Error response like this.
{ERROR Response json struct}
{"code":-208,"message":"Fail to login","internal":"{ERROR Message}","title":null,"btn_text":null} {"code":-208,"message":"Fail to login","internal":"{ERROR Message}","title":null,"btn_text":null}
Whenever I use BodyDump, duplicate error responses are returned, but I can't find the reason.
Below is the source code of the body dump. I commented out all the actual use parts, but simply applying the body dump returns redundant json body data.
func MWBodyDumpForELS() echo.MiddlewareFunc {
config := middleware.BodyDumpConfig{
Skipper: middleware.DefaultSkipper,
Handler: func(c echo.Context, reqBody, resBody []byte) {
//session, _ := c.Get(SESSION_KEY).(*service.Session)
//go els.Send(c.Path(), session.UserID, session.MembershipType, c.RealIP(), c.Request().RequestURI,
// c.Request().UserAgent(), c.Request().Method, c.Response().Header().Get("Content-Type"),
// c.QueryParams(), c.Response().Status, reqBody, resBody)
return
},
}
return middleware.BodyDumpWithConfig(config)
}
The HTTPErrorHandler I defined is It only seems to happen whenever it returns to that case.:
func HTTPErrorHandler(err error, c echo.Context) {
// code := http.StatusInternalServerError
switch t := err.(type) {
case *echo.HTTPError:
c.JSON(t.Code, NewAPIError(t.Code, fmt.Sprint(t.Message)).internal(t.Internal))
case *APIError:
c.JSON(t.httpCode, t)
default:
if err == nil {
c.JSON(http.StatusInternalServerError, ErrUnknown)
} else {
c.JSON(http.StatusInternalServerError, ErrUnknown.internal(err.Error()))
}
}
}
Someone know anything about this?
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
I am trying to build and test a very basic API in Go to learn more about the language after following their tutorial. The API and the four routes defined work in Postman and the browser, but when trying to write the test for any of the routes, the ResponseRecorder doesn't have a body, so I cannot verify it is correct.
I followed the example here and it works, but when I change it for my route, there is no response.
Here is my main.go file.
package main
import (
"encoding/json"
"fmt"
"log"
"net/http"
"github.com/gorilla/mux"
)
// A Person represents a user.
type Person struct {
ID string `json:"id,omitempty"`
Firstname string `json:"firstname,omitempty"`
Lastname string `json:"lastname,omitempty"`
Location *Location `json:"location,omitempty"`
}
// A Location represents a Person's location.
type Location struct {
City string `json:"city,omitempty"`
Country string `json:"country,omitempty"`
}
var people []Person
// GetPersonEndpoint returns an individual from the database.
func GetPersonEndpoint(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(req)
for _, item := range people {
if item.ID == params["id"] {
json.NewEncoder(w).Encode(item)
return
}
}
json.NewEncoder(w).Encode(&Person{})
}
// GetPeopleEndpoint returns all people from the database.
func GetPeopleEndpoint(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
json.NewEncoder(w).Encode(people)
}
// CreatePersonEndpoint creates a new person in the database.
func CreatePersonEndpoint(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(req)
var person Person
_ = json.NewDecoder(req.Body).Decode(&person)
person.ID = params["id"]
people = append(people, person)
json.NewEncoder(w).Encode(people)
}
// DeletePersonEndpoint deletes a person from the database.
func DeletePersonEndpoint(w http.ResponseWriter, req *http.Request) {
w.Header().Set("Content-Type", "application/json")
params := mux.Vars(req)
for index, item := range people {
if item.ID == params["id"] {
people = append(people[:index], people[index+1:]...)
break
}
}
json.NewEncoder(w).Encode(people)
}
// SeedData is just for this example and mimics a database in the 'people' variable.
func SeedData() {
people = append(people, Person{ID: "1", Firstname: "John", Lastname: "Smith", Location: &Location{City: "London", Country: "United Kingdom"}})
people = append(people, Person{ID: "2", Firstname: "John", Lastname: "Doe", Location: &Location{City: "New York", Country: "United States Of America"}})
}
func main() {
router := mux.NewRouter()
SeedData()
router.HandleFunc("/people", GetPeopleEndpoint).Methods("GET")
router.HandleFunc("/people/{id}", GetPersonEndpoint).Methods("GET")
router.HandleFunc("/people/{id}", CreatePersonEndpoint).Methods("POST")
router.HandleFunc("/people/{id}", DeletePersonEndpoint).Methods("DELETE")
fmt.Println("Listening on http://localhost:12345")
fmt.Println("Press 'CTRL + C' to stop server.")
log.Fatal(http.ListenAndServe(":12345", router))
}
Here is my main_test.go file.
package main
import (
"fmt"
"net/http"
"net/http/httptest"
"testing"
)
func TestGetPeopleEndpoint(t *testing.T) {
req, err := http.NewRequest("GET", "/people", nil)
if err != nil {
t.Fatal(err)
}
// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetPeopleEndpoint)
// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
handler.ServeHTTP(rr, req)
// Trying to see here what is in the response.
fmt.Println(rr)
fmt.Println(rr.Body.String())
// Check the status code is what we expect.
if status := rr.Code; status != http.StatusOK {
t.Errorf("handler returned wrong status code: got %v want %v",
status, http.StatusOK)
}
// Check the response body is what we expect - Commented out because it will fail because there is no body at the moment.
// expected := `[{"id":"1","firstname":"John","lastname":"Smith","location":{"city":"London","country":"United Kingdom"}},{"id":"2","firstname":"John","lastname":"Doe","location":{"city":"New York","country":"United States Of America"}}]`
// if rr.Body.String() != expected {
// t.Errorf("handler returned unexpected body: got %v want %v",
// rr.Body.String(), expected)
// }
}
I appreciate that I am probably making a beginner mistake, so please take mercy on me. I have read a number of blogs testing mux, but can't see what I have done wrong.
Thanks in advance for your guidance.
UPDATE
Moving my SeeData call to init() resolved the body being empty for the people call.
func init() {
SeedData()
}
However, I now have no body returned when testing a specific id.
func TestGetPersonEndpoint(t *testing.T) {
id := 1
path := fmt.Sprintf("/people/%v", id)
req, err := http.NewRequest("GET", path, nil)
if err != nil {
t.Fatal(err)
}
// We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
rr := httptest.NewRecorder()
handler := http.HandlerFunc(GetPersonEndpoint)
// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
handler.ServeHTTP(rr, req)
// Check request is made correctly and responses.
fmt.Println(path)
fmt.Println(rr)
fmt.Println(req)
fmt.Println(handler)
// expected response for id 1.
expected := `{"id":"1","firstname":"John","lastname":"Smith","location":{"city":"London","country":"United Kingdom"}}` + "\n"
if status := rr.Code; status != http.StatusOK {
message := fmt.Sprintf("The test returned the wrong status code: got %v, but expected %v", status, http.StatusOK)
t.Fatal(message)
}
if rr.Body.String() != expected {
message := fmt.Sprintf("The test returned the wrong data:\nFound: %v\nExpected: %v", rr.Body.String(), expected)
t.Fatal(message)
}
}
Moving my SeedData call to init() resolved the body being empty for the people call.
func init() {
SeedData()
}
Creating a new router instance resolved the issue with accessing a variable on a route.
rr := httptest.NewRecorder()
router := mux.NewRouter()
router.HandleFunc("/people/{id}", GetPersonEndpoint)
router.ServeHTTP(rr, req)
I think its because your test isn't including the router hence the path variables aren't being detected. Here, try this
// main.go
func router() *mux.Router {
router := mux.NewRouter()
router.HandleFunc("/people", GetPeopleEndpoint).Methods("GET")
router.HandleFunc("/people/{id}", GetPersonEndpoint).Methods("GET")
router.HandleFunc("/people/{id}", CreatePersonEndpoint).Methods("POST")
router.HandleFunc("/people/{id}", DeletePersonEndpoint).Methods("DELETE")
return router
}
and in your testcase, initiatize from the router method like below
handler := router()
// Our handlers satisfy http.Handler, so we can call their ServeHTTP method
// directly and pass in our Request and ResponseRecorder.
handler.ServeHTTP(rr, req)
And now if you try accessing the path variable id, it should be present in the map retured by mux since mux registered it when you initiatlized the Handler from mux Router instance returned from router()
params := mux.Vars(req)
for index, item := range people {
if item.ID == params["id"] {
people = append(people[:index], people[index+1:]...)
break
}
}
Also like you mentioned, use the init function for one time setups.
// main.go
func init(){
SeedData()
}
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(...) ... { ... }