Chapter 9. Give REST to Your Application with Ajax

REST stands for REpresentational State Transfer; REST is an architectural style. Everything in REST is considered as a resource and every resource is identified by a URI. RESTful web services have been embraced by large service providers across the Web as an alternative to SOAP-based Web Services due to their simplicity.

After finishing this chapter, you will have a clear idea about:

  • REST web services
  • Ajax

Introduction to REST

As I already mentioned, in a REST-based application, everything including static resources, data, and operations is considered as a resource and identified by a URI. For example, consider a piece of functionality to add a new product to our store; we can represent that operation by a URI, something such as http://localhost:8080/webstore/products/add, and we can pass the new product details in XML or JSON representation to that URL. So, in REST, URIs are used to connect clients and servers to exchange resources in the form of representations (HTML, XML, JSON, and so on). In order to exchange data, REST relies on basic HTTP protocol methods GET, POST, PUT, and DELETE.

Spring provides extensive support for developing REST-based web services. In our previous chapters, we have seen that, whenever a web request comes in, we returned a web page to serve that request; usually that web page contained some states (that is, dynamic data). However, in REST-based applications, we only return the states and it is up to the client to decide how to render or present the data to the end user.

Usually, REST-based web services return data in two formats, XML and JSON. We are going to develop some REST-based web services that can return data in the JSON format. After we get the JSON data, we are going to render that data as an HTML page using some JavaScript libraries in the browser.

In our webstore application, we have successfully listed some of the products, but the store cannot make a profit without enabling the end user to pick up some products in his/her shopping cart. So let's add a shopping cart facility to our store.

Time for action - implementing RESTful web services

We are going to add the shopping cart facility in two stages. Firstly, we will create a REST-style Controller to handle all shopping cart-related web service requests. Secondly, we will add some JavaScript code to render the JSON data returned by the REST web Service Controller. So first, let's implement some RESTful web services using Spring MVC Controllers, so that later we can add some JavaScript code to consume those web services:

  1. Add the following schema definitions for CART and CART_ITEM tables in create-table.sql. You can find create-table.sql under the folder structure src/main/resources/db/sql:
          CREATE TABLE CART ( 
             ID VARCHAR(50) PRIMARY KEY 
          ); 
     
          CREATE TABLE CART_ITEM ( 
             ID VARCHAR(75), 
             PRODUCT_ID VARCHAR(25) FOREIGN KEY REFERENCES  
          PRODUCTS(ID),  
             CART_ID varchar(50) FOREIGN KEY REFERENCES  
          CART(ID), 
             QUANTITY BIGINT, 
             CONSTRAINT CART_ITEM_PK PRIMARY KEY (ID,CART_ID)  
          ); 
    
  2. Add the following drop table command as the first line in create-table.sql:
          DROP TABLE CART_ITEM IF EXISTS; 
          DROP TABLE CART IF EXISTS; 
    
  3. Create a domain class named CartItem under the package com.packt.webstore.domain in the source folder src/main/java, and add the following code into it:
          package com.packt.webstore.domain; 
     
          import java.io.Serializable; 
          import java.math.BigDecimal; 
     
          public class CartItem implements Serializable{ 
     
             private static final long serialVersionUID = - 
             4546941350577482213L; 
     
             private String id; 
             private Product product; 
             private int quantity; 
             private BigDecimal totalPrice; 
     
             public CartItem(String id) { 
                super(); 
                this.id = id; 
             } 
     
             public String getId() { 
                return id; 
             } 
     
             public Product getProduct() { 
                return product; 
             } 
     
             public void setProduct(Product product) { 
                this.product = product; 
                this.updateTotalPrice(); 
             } 
     
             public int getQuantity() { 
                return quantity; 
             } 
     
             public void setQuantity(int quantity) { 
                this.quantity = quantity; 
             } 
     
             public BigDecimal getTotalPrice() { 
                this.updateTotalPrice(); 
                return totalPrice; 
             } 
     
             public void updateTotalPrice() { 
                totalPrice = 
          this.product.getUnitPrice().multiply(new  
          BigDecimal(this.quantity)); 
             } 
     
             @Override 
             public int hashCode() { 
                final int prime = 31; 
                int result = 1; 
                result = prime * result + ((id == null) ? 0 :  
          id.hashCode()); 
                return result; 
             } 
     
             @Override 
             public boolean equals(Object obj) { 
                if (this == obj) 
                   return true; 
                if (obj == null) 
                   return false; 
                if (getClass() != obj.getClass()) 
                   return false; 
                CartItem other = (CartItem) obj; 
                if (id == null) { 
                   if (other.id != null) 
                      return false; 
                } else if (!id.equals(other.id)) 
                   return false; 
                return true; 
             } 
          } 
    
  4. Similarly, add one more domain class named Cart in the same package and add the following code into it:
          package com.packt.webstore.domain; 
     
          import java.io.Serializable; 
          import java.math.BigDecimal; 
          import java.util.List; 
          import java.util.function.Function; 
     
          public class Cart implements Serializable{ 
     
             private static final long serialVersionUID =  
          6554623865768217431L; 
     
             private String id; 
             private List<CartItem> cartItems; 
             private BigDecimal grandTotal; 
     
             public Cart(String id) { 
                this.id = id; 
             } 
     
             public String getId() { 
                return id; 
             } 
     
             public BigDecimal getGrandTotal() { 
                updateGrandTotal(); 
                return grandTotal; 
             } 
     
             public void setGrandTotal(BigDecimal grandTotal) { 
                this.grandTotal = grandTotal; 
             } 
     
             public List<CartItem> getCartItems() { 
                return cartItems; 
             } 
     
           public void setCartItems(List<CartItem> cartItems) { 
          this.cartItems = cartItems; 
             } 
     
             public CartItem getItemByProductId(String  
          productId) { 
                return cartItems.stream().filter(cartItem ->  
          cartItem.getProduct().getProductId() 
          .equals(productId)) 
                                   .findAny() 
                                   .orElse(null); 
             } 
     
             public void updateGrandTotal() { 
     
                Function<CartItem, BigDecimal> totalMapper =  
          cartItem -> cartItem.getTotalPrice(); 
     
                BigDecimal grandTotal = cartItems.stream() 
                                           .map(totalMapper) 
                   .reduce(BigDecimal.ZERO, BigDecimal::add); 
     
                this.setGrandTotal(grandTotal); 
             } 
     
             @Override 
             public int hashCode() { 
                final int prime = 31; 
                int result = 1; 
                result = prime * result + ((id == null) ? 0 :  
          id.hashCode()); 
                return result; 
             } 
     
             @Override 
             public boolean equals(Object obj) { 
                if (this == obj) 
                   return true; 
                if (obj == null) 
                   return false; 
                if (getClass() != obj.getClass()) 
                   return false; 
                Cart other = (Cart) obj; 
                if (id == null) { 
                   if (other.id != null) 
                      return false; 
                } else if (!id.equals(other.id)) 
                   return false; 
                return true; 
             } 
          } 
    
  5. Create a data transfer object (dto) named CartItemDto under the package com.packt.webstore.dto in the source folder src/main/java, and add the following code into it:
          package com.packt.webstore.dto; 
     
          import java.io.Serializable; 
     
          public class CartItemDto implements Serializable{ 
     
             private static final long serialVersionUID = - 
          3551573319376880896L; 
     
             private String id; 
             private String productId; 
             private int quantity; 
        
             public String getId() { 
                return id; 
             } 
     
             public void setId(String id) { 
                this.id = id; 
             } 
     
             public String getProductId() { 
                return productId; 
             } 
              
             public void setProductId(String productId) { 
                this.productId = productId; 
             } 
     
             public int getQuantity() { 
                return quantity; 
             } 
     
             public void setQuantity(int quantity) { 
                this.quantity = quantity; 
             } 
          } 
    
  6. Similarly, add one more dto object named CartDto in the same package and add the following code into it:
          package com.packt.webstore.dto; 
     
          import java.io.Serializable; 
          import java.util.ArrayList; 
          import java.util.List; 
     
          public class CartDto implements Serializable{ 
     
             private static final long serialVersionUID = - 
          2017182726290898588L; 
     
             private String id; 
             private List<CartItemDto> cartItems; 
     
             public CartDto() {} 
     
             public CartDto(String id) { 
                this.id = id; 
                cartItems = new ArrayList<>(); 
             } 
     
             public String getId() { 
                return id; 
             } 
     
             public void setId(String id) { 
                this.id = id; 
             } 
     
             public List<CartItemDto> getCartItems() { 
                return cartItems; 
             } 
     
             public void setCartItems(List<CartItemDto>  
          cartItems) { 
                this.cartItems = cartItems; 
             } 
     
             public void addCartItem(CartItemDto cartItemDto) { 
                this.cartItems.add(cartItemDto); 
             } 
          } 
    
  7. Create a class named CartItemMapper under the package com.packt.webstore.domain.repository.impl in the source folder src/main/java and add the following code into it:
          package com.packt.webstore.domain.repository.impl; 
     
          import java.sql.ResultSet; 
          import java.sql.SQLException; 
     
          import org.springframework.jdbc.core.RowMapper; 
     
          import com.packt.webstore.domain.CartItem; 
          import com.packt.webstore.service.ProductService; 
     
          public class CartItemMapper implements  
          RowMapper<CartItem> { 
             private ProductService productService; 
        
             public CartItemMapper(ProductService  
          productService) { 
                this.productService = productService; 
             } 
     
             @Override 
             public CartItem mapRow(ResultSet rs, int rowNum)  
          throws SQLException { 
                CartItem cartItem = new  
          CartItem(rs.getString("ID")); 
           
          cartItem.setProduct(productService.getProductById 
           (rs.getString("PRODUCT_ID"))); 
                cartItem.setQuantity(rs.getInt("QUANTITY")); 
     
                return cartItem; 
             } 
          } 
    
  8. Create a class named CartMapper under the package com.packt.webstore.domain.repository.impl in the source folder src/main/java and add the following code into it:
          package com.packt.webstore.domain.repository.impl; 
     
          import java.sql.ResultSet; 
          import java.sql.SQLException; 
          import java.util.List; 
     
          import org.springframework.jdbc.core.RowMapper; 
          import org.springframework.jdbc.core.namedparam 
          .NamedParameterJdbcTemplate; 
     
          import com.packt.webstore.domain.Cart; 
          import com.packt.webstore.domain.CartItem; 
          import com.packt.webstore.service.ProductService; 
     
          public class CartMapper implements RowMapper<Cart> { 
             private CartItemMapper cartItemMapper; 
             private NamedParameterJdbcTemplate jdbcTempleate; 
        
             public CartMapper(NamedParameterJdbcTemplate  
          jdbcTempleate, ProductService productService) { 
                this.jdbcTempleate = jdbcTempleate; 
                cartItemMapper = new  
          CartItemMapper(productService); 
             } 
     
             public Cart mapRow(ResultSet rs, int rowNum)  
          throws SQLException { 
                String id = rs.getString("ID"); 
                  Cart cart = new Cart(id); 
             
                  String SQL = String.format("SELECT * FROM  
          CART_ITEM WHERE CART_ID = '%s'", id); 
                  List<CartItem> cartItems =  
          jdbcTempleate.query(SQL, cartItemMapper); 
                  cart.setCartItems(cartItems); 
                  return cart; 
             } 
          } 
    
  9. Create an interface named CartRepository under the package com.packt.webstore.domain.repository in the source folder src/main/java and add the following method declarations into it:
          package com.packt.webstore.domain.repository; 
     
          import com.packt.webstore.domain.Cart; 
          import com.packt.webstore.dto.CartDto; 
     
          public interface CartRepository { 
     
             void create(CartDto cartDto); 
        
             Cart read(String id); 
        
             void update(String id, CartDto cartDto); 
        
             void delete(String id); 
     
             void addItem(String cartId, String productId); 
     
             void removeItem(String cartId, String productId); 
          } 
    
  10. Create an implementation class named InMemoryCartRepository for the previous interface under the package com.packt.webstore.domain.repository.impl in the source folder src/main/java and add the following code into it:
          package com.packt.webstore.domain.repository.impl; 
     
          import java.util.HashMap; 
          import java.util.List; 
          import java.util.Map; 
     
          import org.springframework.beans.factory 
          .annotation.Autowired; 
          import org.springframework.dao 
          .EmptyResultDataAccessException; 
          import org.springframework.jdbc.core.namedparam 
          .NamedParameterJdbcTemplate; 
          import org.springframework.stereotype.Repository; 
     
          import com.packt.webstore.domain.Cart; 
          import com.packt.webstore.domain.CartItem; 
          import com.packt.webstore.domain.Product; 
          import com.packt.webstore.domain. 
          repository.CartRepository; 
          import com.packt.webstore. 
          dto.CartDto; 
          import com.packt.webstore.dto.CartItemDto; 
          import com.packt.webstore.service.ProductService; 
     
          @Repository 
          public class InMemoryCartRepository implements  
          CartRepository{ 
        
             @Autowired 
             private NamedParameterJdbcTemplate jdbcTempleate; 
        
             @Autowired 
             private ProductService productService; 
        
             public void create(CartDto cartDto) { 
           
                String INSERT_CART_SQL = "INSERT INTO CART(ID)  
          VALUES (:id)"; 
     
                Map<String, Object> cartParams = new  
          HashMap<String, Object>(); 
          cartParams.put("id", cartDto.getId()); 
              
                jdbcTempleate.update(INSERT_CART_SQL,  
          cartParams);      
     
          cartDto.getCartItems().stream() 
          .forEach(cartItemDto ->{ 
              
             Product productById =  
          productService.getProductById 
           (cartItemDto.getProductId()); 
              
                   String INSERT_CART_ITEM_SQL =  
                "INSERT INTO CART_ITEM(ID,PRODUCT_ID 
                ,CART_ID,QUANTITY) " 
                                        + "VALUES (:id,  
          :product_id, :cart_id, :quantity)"; 
     
                  Map<String, Object> cartItemsParams = new  
          HashMap<String, Object>(); 
                   cartItemsParams.put("id",  
          cartItemDto.getId()); 
                   cartItemsParams.put("product_id",  
          productById.getProductId()); 
                   cartItemsParams.put("cart_id",  
          cartDto.getId()); 
                   cartItemsParams.put("quantity",  
          cartItemDto.getQuantity()); 
              
                   jdbcTempleate.update(INSERT_CART_ITEM_SQL,  
          cartItemsParams);      
                });     
            } 
        
            public Cart read(String id) { 
              String SQL = "SELECT * FROM CART WHERE ID = :id";   
               Map<String, Object> params = new HashMap<String,  
          Object>(); 
                params.put("id", id);   
                CartMapper cartMapper = new  
          CartMapper(jdbcTempleate, productService); 
           
                try { 
                   return jdbcTempleate.queryForObject(SQL,  
          params, cartMapper); 
                } catch (EmptyResultDataAccessException e) { 
                     return null; 
               }       
             } 
        
        
            @Override 
            public void update(String id, CartDto cartDto) { 
           
               List<CartItemDto> cartItems =  
          cartDto.getCartItems(); 
                for(CartItemDto cartItem :cartItems) { 
              
                  String   SQL = "UPDATE CART_ITEM SET QUANTITY  
          = :quantity,  PRODUCT_ID = :productId WHERE ID = :id  
          AND CART_ID = :cartId"; 
                  Map<String, Object> params = new  
          HashMap<String, Object>(); 
                   params.put("id", cartItem.getId()); 
                   params.put("quantity",  
          cartItem.getQuantity());   
                   params.put("productId",  
          cartItem.getProductId());   
                   params.put("cartId", id);   
           
                  jdbcTempleate.update(SQL, params);   
                }    
            } 
     
             @Override 
            public void delete(String id) { 
                String SQL_DELETE_CART_ITEM = "DELETE FROM  
          CART_ITEM WHERE CART_ID = :id"; 
                String SQL_DELETE_CART = "DELETE FROM CART  
          WHERE ID = :id"; 
           
                Map<String, Object> params = new  
          HashMap<String, Object>(); 
                params.put("id", id); 
        
                jdbcTempleate.update(SQL_DELETE_CART_ITEM,  
          params);   
                jdbcTempleate.update(SQL_DELETE_CART, params);   
            } 
        
            @Override 
            public void addItem(String cartId, String  
          productId) { 
           
                String SQL=null; 
               Cart cart = null; 
           
                cart = read(cartId); 
                if(cart ==null) { 
                   CartItemDto newCartItemDto = new  
          CartItemDto(); 
                   newCartItemDto.setId(cartId+productId); 
                  newCartItemDto.setProductId(productId); 
                  newCartItemDto.setQuantity(1); 
              
                  CartDto newCartDto = new CartDto(cartId); 
                   newCartDto.addCartItem(newCartItemDto); 
                   create(newCartDto); 
                   return; 
                } 
           
               Map<String, Object> cartItemsParams = new  
          HashMap<String, Object>(); 
     
               if(cart.getItemByProductId(productId) == null) { 
                   SQL = "INSERT INTO CART_ITEM (ID,  
          PRODUCT_ID, CART_ID, QUANTITY) VALUES (:id,  
          :productId, :cartId, :quantity)"; 
                  cartItemsParams.put("id", cartId+productId); 
                   cartItemsParams.put("quantity", 1); 
                } else { 
                  SQL = "UPDATE CART_ITEM SET QUANTITY =  
          :quantity WHERE CART_ID = :cartId AND PRODUCT_ID =  
          :productId"; 
                   CartItem existingItem =  
          cart.getItemByProductId(productId); 
                   cartItemsParams.put("id",  
          existingItem.getId()); 
                   cartItemsParams.put("quantity",  
          existingItem.getQuantity()+1); 
               } 
           
               cartItemsParams.put("productId", productId); 
               cartItemsParams.put("cartId", cartId); 
           
               jdbcTempleate.update(SQL, cartItemsParams);      
            } 
     
             @Override 
             public void removeItem(String cartId, String  
          productId) { 
                String SQL_DELETE_CART_ITEM = "DELETE FROM  
          CART_ITEM WHERE PRODUCT_ID = :productId AND CART_ID =  
          :id"; 
           
                Map<String, Object> params = new  
          HashMap<String, Object>();    
               params.put("id", cartId); 
                params.put("productId", productId); 
           
                jdbcTempleate.update(SQL_DELETE_CART_ITEM,  
          params);   
             } 
          } 
    
  11. Create an interface named CartService under the package com.packt.webstore.service in the source folder src/main/java and add the following method declarations into it:
          package com.packt.webstore.service; 
          import com.packt.webstore.domain.Cart; 
          import com.packt.webstore.dto.CartDto; 
     
          public interface CartService { 
        
             void create(CartDto cartDto); 
        
             Cart read(String cartId); 
        
             void update(String cartId, CartDto cartDto); 
        
            void delete(String id); 
     
            void addItem(String cartId, String productId); 
     
             void removeItem(String cartId, String productId); 
          } 
    
  12. Create an implementation class named CartServiceImpl for the preceding interface under the package com.packt.webstore.service.impl in the source folder src/main/java and add the following code into it:
          package com.packt.webstore.service.impl; 
     
          import org.springframework.beans.factory. 
          annotation.Autowired; 
          import org.springframework.stereotype.Service; 
     
          import com.packt.webstore.domain.Cart; 
          import com.packt.webstore.domain 
          .repository.CartRepository; 
          import com.packt.webstore.dto.CartDto; 
          import com.packt.webstore.service.CartService; 
     
          @Service 
          public class CartServiceImpl implements CartService{ 
        
             @Autowired 
             private CartRepository cartRepository; 
     
             public void create(CartDto cartDto) { 
                cartRepository.create(cartDto); 
             } 
     
            @Override 
            public Cart read(String id) { 
                return cartRepository.read(id); 
             } 
     
             @Override 
             public void update(String id, CartDto cartDto) { 
               cartRepository.update(id, cartDto); 
             } 
     
            @Override 
            public void delete(String id) { 
                cartRepository.delete(id); 
            } 
     
             @Override 
            public void addItem(String cartId, String  
          productId) { 
                cartRepository.addItem(cartId, productId); 
            } 
     
            @Override 
             public void removeItem(String cartId, String  
          productId) { 
                cartRepository.removeItem(cartId, productId); 
             } 
          } 
    
  13. Now create a class named CartRestController under the package com.packt.webstore.controller in the source folder src/main/java and add the following code into it:
          package com.packt.webstore.controller; 
     
          import javax.servlet.http.HttpSession; 
     
          import org.springframework.beans.factory 
          .annotation.Autowired; 
          import org.springframework.http.HttpStatus; 
          import org.springframework.web.bind 
          .annotation.PathVariable; 
          import org.springframework.web.bind 
          .annotation.RequestBody; 
          import org.springframework.web.bind 
          .annotation.RequestMapping; 
          import org.springframework.web.bind 
          .annotation.RequestMethod; 
          import org.springframework.web.bind 
          .annotation.ResponseStatus; 
          import org.springframework.web.bind 
          .annotation.RestController; 
     
          import com.packt.webstore.domain.Cart; 
          import com.packt.webstore.dto.CartDto; 
          import com.packt.webstore.service.CartService; 
     
          @RestController 
          @RequestMapping(value = "rest/cart") 
          public class CartRestController { 
     
             @Autowired 
             private CartService cartService; 
        
             @RequestMapping(method = RequestMethod.POST) 
             @ResponseStatus(value = HttpStatus.CREATED) 
             public void create(@RequestBody CartDto cartDto) { 
                cartService.create(cartDto); 
             } 
     
             @RequestMapping(value = "/{cartId}", method =  
          RequestMethod.GET) 
            public Cart read(@PathVariable(value = "cartId")  
          String cartId) { 
                return cartService.read(cartId); 
             } 
     
             @RequestMapping(value = "/{cartId}", method =  
          RequestMethod.PUT) 
            @ResponseStatus(value = HttpStatus.OK) 
            public void update(@PathVariable(value = "cartId")  
          String cartId, @RequestBody CartDto cartDto) { 
               cartDto.setId(cartId); 
                cartService.update(cartId, cartDto); 
            } 
     
             @RequestMapping(value = "/{cartId}", method =        
          RequestMethod.DELETE) 
             @ResponseStatus(value = HttpStatus.OK) 
             public void delete(@PathVariable(value = "cartId")  
          String cartId) { 
                cartService.delete(cartId); 
             } 
           
            @RequestMapping(value = "/add/{productId}", method  
          = RequestMethod.PUT) 
            @ResponseStatus(value = HttpStatus.OK) 
             public void addItem(@PathVariable String  
          productId, HttpSession session) { 
               cartService.addItem(session.getId(),productId); 
             } 
        
             @RequestMapping(value = "/remove/{productId}",  
          method = RequestMethod.PUT) 
             @ResponseStatus(value = HttpStatus.OK) 
             public void removeItem(@PathVariable String  
          productId, HttpSession session) { 
          cartService.removeItem(session.getId(),productId); 
             } 
          } 
    
  14. Now run our webstore project from the STS.

What just happened?

Okay, in order to store cart-related information, first of all we need the database tables to store cart-related information, which is why we are creating CART and CART_ITEM tables in steps 1 and 2.

In steps 3 and 4, we have just created two domain classes called CartItem and Cart to hold the information about the shopping cart. The CartItem class just represents a single item in a shopping cart and it holds information such as the product, quantity, and the totalPrice. Similarly, the Cart represents the whole shopping cart itself; a Cart can have a collection of cartItems and grandTotal. Similarly, in steps 5 and 6 we have created two more data transfer objects called CartItemDto and CartDto to carry data between the REST client and our backend services.

In steps 7 and 8 we created a class called CartItemMapper and CartMapper, which we are going to use to map the database records to the CartItem and Cart domain object in the repository classes.

In steps 9 to 10, we just created a repository layer to manage CartDto objects. In the CartRepository interface, we have defined six methods to take care of CRUD operations (Create, Read, Update, and Delete) on the CART_ITEM table. The InMemoryCartRepository is just an implementation of the CartRepository interface.

Similarly, from steps 11 to 12, we have created the service layer for Cart objects. The CartService interface has the same methods of the CartRepository interface. The CartServiceImpl class just internally uses InMemoryCartRepository to carry out all the CRUD operations.

Step 13 is very crucial in the whole sequence because in that step we have created our REST-style Controller to handle all Cart-related REST web services. The CartRestController class mainly has six methods to handle web requests for CRUD operations on Cart objects, namely create, read, update, and delete, and two more methods, addItem and removeItem, to handle adding and removing a CartItem from the Cart object. We will explore the first four CRUD methods in greater details.

You will see, on the surface, the CartRestController class is just like any other normal Spring MVC Controller, because it has the same @RequestMapping annotations. What makes it special enough to become a REST-style Controller is the @RestController and @RequestBody annotations. See the following Controller method:

@RequestMapping(value = "/{cartId}", method = RequestMethod.GET) 
public Cart read(@PathVariable(value = "cartId") String cartId) { 
   return cartService.read(cartId); 
} 

Usually every Controller method is used to return a View name, so that the dispatcher servlet can find the appropriate View file and dispatch that View file to the client, but here we have returned the object (Cart) itself. Instead of putting the object into the Model, we have returned the object itself. Why? Because we want to return the state of the object in JSON format. Remember, REST-based web services should return data in JSON or XML format and the client can use the data however they want; they may render it to an HTML page, or they may send it to some external system as it is, as raw JSON/XML data.

Okay, let's come to the point. The read Controller method is just returning Cart Java objects; how come this Java object is being converted into JSON or XML format? This is where the @RestController annotation comes in. The @RestController annotation instructs Spring to convert all Java objects that are returned from the request-mapping methods into JSON/XML format and send a response in the body of the HTTP response.

Similarly, when you send an HTTP request to a Controller method with JSON/XML data in it, the @RequestBody annotation instructs Spring to convert it into the corresponding Java object. That's why the create method has a cartDto parameter annotated with a @RequestBody annotation.

If you closely observe the @RequestMapping annotation of all those six CRUD methods, you will end up with the following table:

URL

HTTP method

Description

http://localhost:8080/webstore/rest/cart

POST

Creates a new cart

http://localhost:8080/webstore/rest/cart/1234

GET

Retrieves cart with ID = 1234

http://localhost:8080/webstore/rest/cart/1234

PUT

Updates cart with ID = 1234

http://localhost:8080/webstore/rest/cart/1234

DELETE

Deletes cart with ID = 1234

http://localhost:8080/webstore/rest/cart/add/P1234

PUT

Adds the product with ID P1234 to the cart under session

http://localhost:8080/webstore/rest/cart/remove/P1234

PUT

Removes the product with ID P1234 to the cart under session

Though the request-mapping URL is more or less the same, based on the HTTP method, we are performing different operations. For example, if you send a GET request to the URL http://localhost:8080/webstore/rest/cart/1234, the read Controller method would get executed and the Cart object with ID 1234 will get returned in JSON format. Similarly, if you send a PUT or DELETE request to the same URL, the update or delete Controller method would get called correspondingly.

In addition to those four CRUD request-mapping methods, we have two more request-mapping methods that take care of adding and removing a CartItem from the Cart object. These methods are considered update methods, which is why both  theaddItem and removeItem methods have PUT as a request method type in their @RequestMapping annotation. For instance, if you send a PUT request to the URL http://localhost:8080/webstore/rest/cart/add/P1236, a product with a product id P1236 will be added to the Cart object. Similarly, if you send a PUT request to the URL http://localhost:8080/webstore/rest/cart/remove/P1236, a product with P1236 will be removed from the Cart object.

Time for action - consuming REST web services

Okay, we have created our REST-style Controller, which can serve some REST-based web requests, but we have not seen our CartRestController in action. Using the standard browser we can only send GET or POST requests; in order to send a PUT or DELETE request we need a special tool. There are plenty of HTTP client tools available to send requests, let's use one such tool called Postman to test our CartRestController. Postman is a Google Chrome browser extension, so better install Google Chrome in your system before downloading the Postman HTTP client:

  1. Go to the Postman download page at http://www.getpostman.com/ from your Google Chrome browser and click Chrome App.
  2. You will be taken to the Chrome webstore page; click the + ADD TO CHROME button to install the Postman tool in your browser:
    Time for action - consuming REST web services

    Postman - HTTP client app installing

  3. Now a Google login page will appear to ask you to log in. Log in using your Google account.
  4. A confirmation dialog will be shown on your screen asking your permission to add the Postman extension to your browser; click the Add app button.
  5. Now open your Google Chrome browser and enter the URL chrome://apps/. A web page will be loaded with all the available apps for your Chrome browser. Just click on the Postman icon to launch the Postman app. Before launching Postman just ensure our webstore project is running.
  6. Now, in the Postman app, enter the request URL as http://localhost:8080/webstore/rest/cart, the request method as POST, the Body as raw format, and the content type as JSON(application/json).
  7. Now enter the following JSON content in the content box and press the Send button. An HTTP respond status 201 will be created:
          { 
             "id": "111", 
             "cartItems" : [ 
                          { 
                              "id" :"1", 
                              "productId":"P1234", 
                              "quantity":1 
                          }, 
                     
                          { 
                              "id" :"2", 
                              "productId":"P1235", 
                              "quantity":2 
                          } 
             ] 
          } 
    
    Time for action - consuming REST web services

    Postman - posting a web request

  8. Now, similarly in the Postman app, enter the target URL as http://localhost:8080/webstore/rest/cart/111 and the request method as GET, and press the Send button. You will get the following JSON as a response:
          { 
            "id": "111", 
            "cartItems": [ 
              { 
                "id": "1", 
                "product": { 
                  "productId": "P1234", 
                  "name": "iPhone 6s", 
                  "unitPrice": 500, 
                  "description": "Apple iPhone 6s smartphone  
          with 4.00-inch 640x1136 display and 8-megapixel rear  
          camera", 
                  "manufacturer": "Apple", 
                  "category": "Smart Phone", 
                  "unitsInStock": 450, 
                  "unitsInOrder": 0, 
                  "discontinued": false, 
                  "condition": "New" 
                }, 
                "quantity": 1, 
                "totalPrice": 500 
              }, 
              { 
                "id": "2", 
                "product": { 
                  "productId": "P1235", 
                  "name": "Dell Inspiron", 
                  "unitPrice": 700, 
                  "description": "Dell Inspiron 14-inch Laptop        
           (Black) with 3rd Generation Intel Core processors", 
                  "manufacturer": "Dell", 
                  "category": "Laptop", 
                  "unitsInStock": 1000, 
                  "unitsInOrder": 0, 
                  "discontinued": false, 
                  "condition": "New" 
                }, 
                "quantity": 2, 
                "totalPrice": 1400 
              } 
            ], 
            "grandTotal": 1900 
          } 
    
  9. To update the cart in the Postman app, enter the target URL as http://localhost:8080/webstore/rest/cart/111 and just change the JSON data. For instance, in the content box, just change the quantity to 3 for the P1234 cart item, choose the request method as PUT and the content type as JSON(application/json), and send the request to the same URL by pressing the Send button:
    Time for action - consuming REST web services

    Postman - posting a PUT web service request.

  10. To verify whether your changes took place, just repeat step 8; you will see the totalPrice increased to 1500 for the cart item with the product ID P1234; grandTotal will increase accordingly as well.
  11. Similarly, to delete the cart just enter the http://localhost:8080/webstore/rest/cart/111 URL in the Postman app, enter the target URL and the request method as DELETE, and press the Send button. You will get the HTTP status 200 (OK) as a response. To verify whether the cart got deleted, just repeat step 8; you will get an empty response.

What just happened?

At the start of the chapter, we discussed that most REST-based web services are designed to exchange data in JSON or XML format. This is because the JSON and XML formats are considered universal formats so that any system can easily understand, parse, and interpret that data. In order to test the REST-based web services that we have created, we need a tool that can send different (GET, PUT, POST, or DELETE) types of HTTP requests with JSON data in its request body. Postman is one such tool and is available as a Google Chrome extension.

From steps 1 to 4, we just installed the Postman app in our Google Chrome browser. In steps 6 and 7, we just sent our first REST-based web request to the target URL http://localhost:8080/webstore/rest/cart to create a new cart in our webstore application. We did this by sending a POST request with the cart information as JSON data to the target URL. If you notice the following JSON data, it represents a cart with a cart id 111, and it has two product (P1234 and P1235) cart items in it:

      { 
         "id": "111", 
         "cartItems" : [ 
                      { 
                          "id" :"1", 
                          "productId":"P1234", 
                          "quantity":1 
                      }, 
                 
                      { 
                          "id" :"2", 
                          "productId":"P1235", 
                          "quantity":2 
                      } 
         ] 
      } 

Since we have posted our first cart in our system, to verify whether that cart got stored in our system, we have sent another REST web request in step 8 to get the whole cart information in JSON format. Notice this time the request type is GET and the target URL is http://localhost:8080/webstore/rest/cart/111 . Remember, we have learned that, in REST-based applications, every resource is identifiable by a URI. Here the URL http://localhost:8080/webstore/rest/cart/111 represents a cart whose ID is 111. If you send a GET request to the previous URL you will get the cart information as JSON data.

Similarly, you can even update the whole cart by sending an updated JSON data as a PUT request to the same URL, which is what we have done in step 9. In a similar fashion we have sent a DELETE request to the same URL to delete the cart whose ID is 111.

Okay, we have tested or consumed our REST-based web services with the help of the Postman HTTP client tool, which is working quite well. But in a real-world application, most of the time these kinds of REST-based web service are consumed from the frontend with the help of a concept called Ajax. Using a JavaScript library, we can send an Ajax request to the backend. In the next section, we will see what Ajax requests are and how to consume REST-based web services using JavaScript/Ajax libraries.

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

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