Handlers

At this point, it is known that routes map target paths to handlers, but what are handlers? Handlers are the meat of web application development. A handler is primarily where the application converts input into output. As mentioned before, net/http defines a handler as an implementation of the http.Handler interface, implementing the ServeHTTP method with the following signature:

func (f Handler) ServeHTTP(w ResponseWriter, r *Request)

Echo has detoured from this model by creating its own interface for an echo.HandlerFunc, defined by the following signature:

type HandlerFunc func(Context) error

Following our example code, the following is an initial attempt at creating a handler for the /login resource of our application. This code is located in handlers/login.go:

// Login - Login Handler will take a username and password from the request
// hash the password, verify it matches in the database and respond with a token
func Login(c echo.Context) error {
        resp := renderings.LoginResponse{}
        lr := new(bindings.LoginRequest)

        if err := c.Bind(lr); err != nil {
                resp.Success = false
                resp.Message = "Unable to bind request for login"
                return c.JSON(http.StatusBadRequest, resp)
        }

        if err := lr.Validate(c); err != nil {
                resp.Success = false
                resp.Message = err.Error()
                return c.JSON(http.StatusBadRequest, resp)
        }

In our example handler function, we begin by setting up a response-rendering structure and a request binding. After we have created our binding structure, we use Echo's built-in Bind function call, which will take the request body payload and deserialize the payload into the structure passed into Bind as a parameter.  After we convert the request payload into a data structure in the handler, we perform some validation on the request-binding data structure. In our case, our data structure has been designed to include a Validate function, as shown in the preceding code.

Now that we have a valid request data structure in our handler code, we can use that request to perform the handler function, which in the case of a typical login handler would be to draw user data from a database and perform hashing on the presented password to authenticate a user:

        // get DB from context
        db := c.Get(models.DBContextKey).(*sql.DB)
        // get user by username from models
        user, err := models.GetUserByUsername(db, lr.Username)
        if err != nil {
                resp.Success = false
                resp.Message = "Username or Password incorrect"
                return c.JSON(http.StatusUnauthorized, resp)
        }

        if err := bcrypt.CompareHashAndPassword(
                user.PasswordHash, []byte(lr.Password)); err != nil {
                resp.Success = false
                resp.Message = "Username or Password incorrect"
                return c.JSON(http.StatusUnauthorized, resp)
        }

Echo provides an easy mechanism for drawing variables from the built-in Context object. With c.Get, we pull the sql database structure pointer from the Echo Context.  We then take that database pointer and use the helper function we created in models to perform a sql request to the database. When we get the result back, which is a models.User structure from the database, we then take the bcrypt hashed password from the database and the plain text password we got from our caller and perform a compare of the two hashed values. Based on the results of that hash comparison, if successful, we then create a JWT token for the user to present in all future requests:

     
        // need to make a token, successful login
        signingKey := c.Get(models.SigningContextKey).([]byte)

        // Create the Claims
        claims := &jwt.StandardClaims{
                ExpiresAt: time.Now().Add(time.Hour * 72).Unix(),
                Issuer:    "service",
        }

        token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
        ss, err := token.SignedString(signingKey)
        if err != nil {
                resp.Success = false
                resp.Message = "Server Error"
                return c.JSON(http.StatusInternalServerError, resp)
        }

        resp.Token = ss

        return c.JSON(http.StatusOK, resp)
}

The preceding code starts the handlers.Login function by creating a new renderings.LoginResponse and a new bindings.LoginRequest, which we then bind our request data from the HTTP request to. Echo has a c.Bind function on the Echo Context type, which takes the input from the request and populates the bindings.LoginRequest we pass in. After we bind and validate our structures, we perform the business logic of getting the user from the database. We perform a bcrypt operation, and, upon success, we generate a new JWT token which will be the authentication token returned to the user.

Handlers are where the business logic happens, and routing is how handlers are run in our service. The essence of a web application or API is confined within these two basic primitives.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.191.237.201