Request binding

Within the context interface in the Echo framework, there is a method called Bind, which will perform request payload binding. The following is the definition of the function:

    // Bind binds the request body into provided type `i`. The default binder
    // does it based on Content-Type header.
    Bind(i interface{}) error

In essence, the Context.Bind function takes the payload and Content-Type header from the HTTP request, and converts it into any structure defined by the passed in i interface{} function parameter. This is a very handy feature of the web application framework as it removes a lot of the coding required by the developer to interpret the request payload themselves. Instead of having to perform deserialization of the HTTP request body yourself, Echo will perform this for you. The following is an example handler that uses the Bind function call, which is located at $GOPATH/src/github.com/PacktPublishing/Echo-Essentials/chapter5/handlers/login.go:

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)
        }
        // ...

As you can see in the preceding example, we are creating a new *bindings.LoginRequest in our project's $GOPATH/src/github.com/PacktPublishing/Echo-Essentials/chapter5/handlers/login.go file, which is the object we want to deserialize from the request payload. The following is the structure of $GOPATH/src/github.com/PacktPublishing/Echo-Essentials/chapter5/bindings/login.go:

type LoginRequest struct {
        Username string `json:"username"`
        Password string `json:"password"`
}

The bindings.LoginRequest structure is comprised of two attributes, a Username and a Password, which in json form are defined as username and password from the struct tag definitions we have outlined in the structure. When we perform a call to our login handler with the following curl command, the lr variable will be populated:

curl -XPOST -H"Content-Type: application/json" localhost:8080/login -d'{"username":"test", "password":"test"}' -D -

Notice the Content-Type: application/json request header we have specified. The following is an example of a valid curl command that uses XML instead of JSON:

curl -XPOST -H"Content-Type: application/xml" localhost:8080/login -d'<LoginRequest><Username>test</Username><Password>test</Password></LoginRequest>' -D - 

Echo comes with built-in content-type awareness for the following content types: application/json, application/xml, and application/x-www-form-urlencoded. As you can see, we were able to create our application without regard to the actual wire encoding of the request. The following is an illustration of the preceding example:

This way, we will be able to support more content types without any additional code out of the box. Content negotiation is valuable to you as an API developer as it makes your application more accessible. By offering your API in multiple content types, you open up your API to more potential integration.

With request binding capabilities, Echo comes out of the box with request binding validation. Request binding validation as a feature of a web application framework gives you the ability to create a common scheme by which all of your API request inputs are validated so that you do not have to clutter your application handler functions with the minutia of how inputs are going to be validated. With the Context.Validate functionality, which is realized in the following example, we are able to create validation on our request input. In order to create a validation scheme where we can clearly define validation code as a method on our request input structures, we first we need to set up an Echo Validator and register it with the Echo framework, which is done by implementing the Validator interface in $GOPATH/src/github.com/PacktPublishing/Echo-Essentials/chapter5/bindings/common.go, as shown in the following code snippet:

type Validatable interface {
        Validate() error
}

var ErrNotValidatable = errors.New("Type is not validatable")

type Validator struct{}

func (v *Validator) Validate(i interface{}) error {
        if validatable, ok := i.(Validatable); ok {
                return validatable.Validate()
        }
        return ErrNotValidatable
}

As seen in the preceding code, our custom Validator structure must implement a Validate method that takes an interface{} as a parameter, which represents the structure to which we have bound our input. Since our preferred design keeps the validation code as close to the structure to which it is validating, our solution presented in this chapter's sample code needs to be as generic as possible.

This particular custom Validator is type checking that the input implements the Validatable interface, and then runs Validate on that type. By creating this new Validatable interface in our project, any structure that has a method called Validate that returns an error type as the response can be fed into the Validator.Validate function. This provides a very slick abstraction that will help organize your input bindings better, and make validation much easier for your project. In order for our request type to implement the Validatable interface, we will update our login bindings in $GOPATH/src/github.com/PacktPublishing/Echo-Essentials/chapter5/bindings/login.go to resemble the following:

package bindings

type LoginRequest struct {
        Username string `json:"username"`
        Password string `json:"password"`
}

func (lr *LoginRequest) Validate() error {
        errs := new(RequestErrors)
        if lr.Username == "" {
                errs.Append(ErrUsernameEmpty)
        }
        if lr.Password == "" {
                errs.Append(ErrPasswordEmpty)
        }
        if errs.Len() == 0 {
                return nil
        }
        return errs
}

Within the preceding example code, you can clearly see that our LoginRequest type now implements the Validatable interface because it has a Validate function that returns an error. Each input binding structure you create for your project will now clearly show how that input structure is validated right next to the structure definition itself. This makes the code much cleaner than if you were to implement this validation code in your handlers each time you bind your request data to a structure.

All we need to do now is to set Echo to use our custom Validator in $GOPATH/src/github.com/PacktPublishing/Echo-Essentials/chapter5/cmd/service/main.go, as shown in the following code:

func main() {                                                                                  
        // create a new echo instance                                                          
        e := echo.New()                                                                        
        e.Logger.SetLevel(log.INFO)                                                           
        e.Validator = new(bindings.Validator) 

        //...

The preceding example shows that by merely setting the Validator attribute on the Echo instance to our new Validator structure, we are able to use the validation capabilities built into the Echo Context to validate inputs within our request handler code. At this point, we are ready to use the Context.Validate method in our handler to validate that our input is valid in handlers/login.go, which will run our binding's validate function that we defined previously. We will insert our validation after our binding occurs, as follows:

//...        
        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 := c.Validate(lr); err != nil {
                resp.Success = false
                resp.Message = err.Error()
                return c.JSON(http.StatusBadRequest, resp)
        }
//...

By using the binding and validation capabilities within Echo, we save time and effort in our application development process. There is no need to worry about the deserialization of the wire format of the request payload, and we can create structure in our code for following best practices for the validation of inputs. Within the preceding handler code, we used the Context.Bind method to read our inputs into the request structure. Afterward, we immediately called Context.Validate and passed in the structure we just bound to perform validation on the structure.

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

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