Building Web Apps with Spring 5 and Angular 4

3.8 (8 reviews total)
By Ajitesh Shukla
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Introduction to Spring Web Framework

About this book

Spring is the most popular application development framework being adopted by millions of developers around the world to create high performing, easily testable, reusable code. Its lightweight nature and extensibility helps you write robust and highly-scalable server-side web applications. Coupled with the power and efficiency of Angular, creating web applications has never been easier.

If you want build end-to-end modern web application using Spring and Angular, then this book is for you.

The book directly heads to show you how to create the backend with Spring, showing you how to configure the Spring MVC and handle Web requests. It will take you through the key aspects such as building REST API endpoints, using Hibernate, working with Junit 5 etc. Once you have secured and tested the backend, we will go ahead and start working on the front end with Angular. You will learn about fundamentals of Angular and Typescript and create an SPA using components, routing etc. Finally, you will see how to integrate both the applications with REST protocol and deploy the application using tools such as Jenkins and Docker.

Publication date:
August 2017
Publisher
Packt
Pages
370
ISBN
9781787284661

 

Chapter 1. Introduction to Spring Web Framework

In this chapter, you will learn about the key concepts of the Spring framework, and how we can use this framework to develop web applications. The following are some of the topics that will be dealt with:

  • Introduction to the Spring IOC container
  • Introduction to the Spring web MVC
  • Building a Hello World web application with Spring Boot
  • Implementing controllers
  • Handling request parameters
  • Handler interceptors
  • Handling responses
  • Creating a RESTful Web Service
  • Dockerizing Spring Boot Application

Starting with the section, Implementing controllers in this chapter, we will start building a sample healthcare web application to demonstrate the key concepts. The following are some of the key functionalities of the sample app which will be covered as part of building the app:

  • Signup for doctors and patients
  • Logging into the app
  • Searching for doctors based on their specialties
  • Fixing an appointment
  • Interacting with the doctor
 

Introduction to the Spring IOC container


As a primer to learning the Spring Web MVC framework, it is recommended to learn some of the following object-oriented design principles, which act as a basis for the Spring Web framework. Note that these principles form a part of the famous Single responsibility, Open-closed, Liskov substitution, Interface segregation, and Dependency inversion (SOLID) principle by Robert Martin (Uncle Bob):

  • Single Responsibility Principle (SIP): This principle states that a module, a class, or a method should have the responsibility of serving just one functionality of the underlying software. In other words, a module, a class or a method should have just one reason to change. Modules or classes following SIP have high cohesiveness, and thus, can be termed as reusable entities. Classes violating SIP are found to have high cyclomatic complexity, and thus, low testability.
  • Open-Closed Principle (OCP): This principle states that the classes are open for extension, but closed for modification. Based on this principle, the core classes of the Spring Web MVC consist of some methods which are marked as final, which, essentially, means that these final methods can not be overridden with custom behavior.
  • Liskov Substitution Principle (LSP): This principle states that if a class A (child class) is derived from class B (parent class), then the object of class B can be replaced by (or substituted with) an object of class A without changing any of the properties of class B. It can be inferred that the functions which use references of the base class must be able to use objects of the derived class without the need to know about the implementation of the base class. For example, let's consider the square and rectangle example. In the case where square derives from rectangle, then, as per LSP, an object of the class Rectangle can be substituted with an object of the class Square. However, in reality, this is not possible without doing appropriate implementation in the Square class setter methods, where setting either of length or breadth sets another side of equal length, and/or code using these classes do appropriate checks to find out whether the object is an instance of the class Square or Rectangle.
  • Interface Segregation Principle (ISP): This principle states that the fat interfaces having large number of API definitions should be split into smaller interfaces defining a set of cohesive APIs. Not following this principle leads to the client providing empty implementations for unwanted APIs.
  • Dependency Inversion Principle (DIP): This principle is pretty much related to the IOC principle, which is discussed in the next section. It states that the dependency relationship between higher-level modules with low-level modules is reversed, thus making these modules independent of each other's implementation details.

Before we get into understanding what is Spring IOC Container, let us quickly learn what is IOC. 

What is IOC?

Many a times, the Hollywood principle of Don't call us, we will call you is used to explain the concept of Inversion of Control. In Hollywood, this is what one gets to hear after auditioning is done. If we apply this principle to the programming model, instead of the classes creating dependencies along with serving functionality, the dependencies are appropriately made available to the classes during runtime, and the classes just need to focus on delivering the functionality.

Simply speaking, Inversion of Control (IOC) is about inverting the flow of control that the traditional programming model used to have in terms of objects at the higher-level handling the creation and management of lower-level objects' (can also be termed as dependencies) life cycle. In the IOC programming model, higher-level objects rather receive one or more instances of these dependencies from the calling object or external framework. This is why IOC is also termed Dependency Injection, wherein the dependencies are injected appropriately, and, objects bother themselves solely with the program execution and not with the object creation. Inversion of Control is related to the object-oriented principle known as the Dependency Inversion Principle (DIP), coined by Robert Martin (Uncle Bob).

Let's take a look at the following code sample, which represents higher-level objects handling the creation of lower-level objects (dependencies):

    /*
     * This class demonstrates the dependency of higher-level object   
     * (DrawWithoutIOC)
     * onto lower level objects such as Rectangle, Square which are 
     * created within 
     * Shape class based on the value of shapeType which is passed as a 
     * method 
     * parameter to draw method.
     */

    public class DrawWithoutIOC {

      Logger logger = Logger.getLogger(DrawWithoutIOC.class.getName());

      public void draw(String shapeType) {
        Shape shape = new Shape();
        try {
          shape.draw(shapeType);
        } 
        catch (UndefinedShapeException e) {
          logger.log(Level.INFO, e.getMessage(), e);
        }
      }
    /*
     * Note that Shape class creates instances of Rectangle or Square   
       class
     * based on the value of shapeType. Any new value that needs to be   
       supported requires change in the draw method of Shape class.
     */

      private class Shape {
        public void draw(String shapeType) throws 
          UndefinedShapeException 
        {
          if(shapeType.equals("rectangle")) {
            Rectangle rectangle = new Rectangle();
            rectangle.draw();
          } else if(shapeType.equals("square")) {
            Square square = new Square();
            square.draw();
          } else {
            String shapeNotSupportedMessage = "Shape " + shapeType + " 
                   not supported";
            logger.log(Level.INFO, shapeNotSupportedMessage);
            throw new UndefinedShapeException 
              (shapeNotSupportedMessage);
          }
        }
      }

      public static void main(String[] args) {
        DrawWithoutIOC drawWithoutIOC = new DrawWithoutIOC();
        drawWithoutIOC.draw("circle");
      }
    }

Let us take a look at the class DrawWithIOC, which accepts the implementation of the Shape object in its public constructor. Note that the dependencies, such as different implementations of the Shape object, are rather injected, and code just does the execution of business logic without bothering about creation of objects related to the different implementations of Shape. Other alternatives to injecting the dependencies are passing arguments to a factory method of the class, or through properties that are set on the object instance:

    /**
     * In this class, the Shape is passed as parameter to DrawWithIOC   
     * constructor. 
     * draw method on a DrawWithIOC object just invokes the draw method  
     * on Shape object. 
     * It, no longer, manage the creation of Shape object based on 
     * shapeType and there upon, invoke the draw method.
     **/

    public class DrawWithIOC {
      Logger logger = Logger.getLogger(DrawWithIOC.class.getName());

      private Shape shape;

      public DrawWithIOC(Shape shape) {
        this.shape = shape;
      }

      public void draw() { 
        this.shape.draw(); 
      } 

      public static void main(String[] args) {
        Shape shape = new Rectangle();
        DrawWithIOC drawWithIOC = new DrawWithIOC(shape);
        drawWithIOC.draw();
      }
    }

What is a Spring IOC container?

A Spring IOC container is a framework which, basically, manages the life cycle of plain old Java objects (POJOs), and injects them into the application as required. Java objects define their dependencies using one of the following methods:

  • Dependencies are passed as arguments to the constructor method of the object. See how the object is passed as an argument to the constructor method in the example cited in the previous section.
  • Dependencies are passed as arguments to the setter method of the object.
  • Dependencies are passed as arguments to a factory method of the object.

A Spring IOC container injects the dependencies after it creates the beans. Note the fact that dependencies are no longer managed by Java objects. They are rather managed and injected by the framework, and hence, Inversion of Control.

The following are the packages which are core to the IOC container:

  • org.springframework.beans
  • org.springframework.context

It is the interface, org.springframework.context.ApplicationContext (sub-interface of the interface, BeanFactory), which represents the Spring IOC container and is responsible for managing the life cycle of beans. The instructions related to creating, configuring, and assembling the objects is termed Configuration Metadata. The configuration metadata is often represented using Java annotations or XML. A Java application using Spring IOC Container is created by combining Business POJOs with the previously mentioned configuration metadata, and passing it on to the IOC Container (an instance of ApplicationContext). The same is represented using the following diagram:

Figure 1: Java Application with Spring IOC Container

Let's illustrate the preceding diagram with an example. The following code represents how a service, namely, UserService is instantiated using Spring IOC container in a Spring Boot web application. Notice how the annotation-based autowiring feature has been used to have ApplicationContext autowired to the userService field in this code:

    package com.example;

    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseBody;

    import com.services.UserService;
    import com.services.domain.User;

    @Controller
    @SpringBootApplication (scanBasePackages={"com.services"})
    public class DemoApplication {

@Autowired
        private UserService userService;

        @RequestMapping("/")
        @ResponseBody
        String home() { 
            User user = null;
            return "Hello " + userService.getUsername(user) + ". How are you?";
        }

        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
 

Introduction to Spring Web MVC


In this section, we will learn the key elements of the Spring Web model-view-controller (Spring Web MVC) framework, and how to quickly get started with a Spring web application using the Spring Web MVC components. The following will be covered in detail in this section:

  • Key building blocks of a Spring Web MVC application
  • Introduction to the Dispatcher servlet

Key building blocks of a Spring Web MVC application

In this section, we will learn about the key building blocks of a Spring web MVC application. The following diagram represents the key building blocks:

Figure 2: Key building blocks of Spring web MVC framework

The following are the details in relation to the preceding diagram:

  • Dispatcher servlet: Dispatcher servlet, also termed the front controller, is at the core of the Spring Web MVC framework. Simply speaking, the Dispatcher servlet determines which controller class and method needs to be called when a page request or an API request arrives. In addition, it sends the response using the appropriate JSP page or JSON/XML objects. It dispatches the incoming requests to the appropriate handlers (custom controllers) with different handler mappings. This is integrated with the Spring IOC container, which allows it to use all the features that the Spring framework provides.
  • Handler Mappings: Handler mappings are used to map the request URL with the appropriate handlers such as controllers. The Dispatcher servlet uses the handler mappings to determine the controllers which will process the incoming requests. The handler mappings are specified in the XML file, or as annotations such as @RequestMapping,  @GetMapping, or @PostMapping, and so on. The following diagram represents the @RequestMapping annotation that is used for URL mapping.
  • Handler Interceptors: Handler interceptors are used to invoke preprocessing and post-processing logic before and after the invocation of the actual handler method respectively.
  • Controllers: These are custom controllers created by the developers and used for processing the incoming requests. The controllers are tagged with annotations such as @Controller or @RestController. Controllers are used to access the application behavior through one or more service interfaces. Controllers are used to interpret the user input, pass them to the services for implementation of business logic, and transform the service output into a model which is presented to the user as a view. The following diagram shows the @Controller annotation which represents the DemoApplication class to play the role of a controller:

Figure 3: Representing handler mappings (URL mapping) and Controller annotation

  • Services: These are the components coded by the developers. These components contain the business logic. One or more methods of services are invoked from within the Controller methods. Spring provides annotations such as @Service for identifying services. The following code represents the service class UserService, which consists of a method, getUsername. Pay attention to the @Service annotation. Note how the instance of UserService is defined in @Controller, as shown in the preceding code, and the method getUsername is invoked on the instance of UserService.
        import org.springframework.beans.factory.annotation.Autowired;
        import org.springframework.stereotype.Service;

        import com.services.dao.UserDAO;
        import com.services.domain.User;

        @Service ("userService")
        public class UserService {

            @Autowired
            private UserDAO userDAO;

            public String getUsername(User user) {
                return userDAO.getUsername(user);
            }
        }
  • Data Access Objects (DAO): The classes which represent DOA, are used to do data processing with the underlying data sources. These classes are annotated with annotations such as @Repository. The preceding code of UserService consists, a DAO, namely, UserDAO. Note the @Repository annotation used by the class, UserDAO, in the following code:
        import org.springframework.stereotype.Repository;

        import com.services.domain.User;

        @Repository ("userDAO")
        public class UserDAO {

            public String getUsername(User user) {
                return "Albert Einstein";
            }
        }
  • View Resolvers: View resolvers are components which map view names to views. They help in rendering models in the browser based on different view technologies such as JSP, FreeMarker, JasperResports, Tiles, Velocity, XML, and so on. Spring comes with different view resolvers such as InternalResourceViewResolver, ResourceBundleViewResolver, XMLViewResolver, and others. View resolvers are used by the Dispatcher servlet to invoke the appropriate view components.
  • Views: Views are used to render the response data on the UI. They can be represented using different technologies such as JSP, Velocity, Tiles, and so on.

Introduction to the Dispatcher servlet

In this section, we will learn about the core component of a Spring web MVC application, the Dispatcher servlet.

As introduced in the previous section, Dispatcher servlet is the front controller which processes the user requests by invoking the appropriate components such as handler mappings, interceptors, adapters, custom controllers, services, DOA, view resolver, and finally, the views. The following diagram represents the web request/response workflow of the Dispatcher servlet:

Figure 4: Web request/response flow across the Dispatcher servlet

As per the preceding diagram, the journey of a request/response through a Spring web MVC application looks like the following:

  1. The user requests arrive at the server, and are intercepted by the Dispatcher servlet.
  2. The Dispatcher servlet gets a handler object (primarily, an instance of HandlerExecutionChain) from the HandlerMapping object based on the URL mapping. URL mappings can be defined with the web.xml file or as annotations on the Controllers' methods.
  1. One or more instances of the HandlerInterceptor objects are retrieved from the handler object, and preprocessing logic is processed on the request object.
  2. The instance of HandlerAdapter is retrieved from the handler object, and the handle method is invoked. This results in execution of logic within the controller class. In the preceding diagram (Figure 3), the request with RequestMapping as "/" leads to the execution of code within the home method as part of this step.  
  3. The post-processing logic on the HandlerInterceptor instances is executed. This is the final step before the rendering method is invoked. 
  4. TheViewResolver instance is used to retrieve the appropriate view component.
  5. The render method is invoked on the instance of view.
 

Building Hello World web application with Spring Boot


In this section, we will learn how to quickly build a web application using Spring Boot. The following will be covered in this section:

  • The Spring Tool Suite (STS) setup in Eclipse IDE
  • Introduction to Spring Boot
  • Building the Hello World web app using Spring Boot

The Spring STS Setup in Eclipse IDE

Spring Tool Suite (STS) provides the development environment for Spring-powered enterprise applications. This can be easily downloaded from the Eclipse marketplace in the following manner:

  1. Within the Eclipse IDE, click on Help | Eclipse Marketplace... and search for Spring STS by submitting Spring STS in the Find text field. The search result would show up different versions of STS for different Eclipse versions. 
  2. Choose the appropriate version and install. The most current version is 3.9.0.RELEASE.
  3. Restart Eclipse, and you are all set to create your first Spring Boot web application.

Introduction to Spring Boot

Spring Boot is a quick and easy way to get up and running with production-grade standalone applications in no time. If you hated all the XML configurations required to be set for creating a Spring web application, Spring Boot helps us to get away with all those troubles, and lets us focus on developing the application from the word go. The following are some of the key attributes of a Spring Boot application:

  • Requires no XML configuration or code generation.
  • Automatically configures Spring wherever appropriate and possible. 
  • Supports embedded web servers such as Tomcat, Jett, and so on. One of the key disadvantages while working with the Spring web framework prior to Spring Boot was deploying these apps explicitly on the web server either manually, or using some tools/scripts. This is no more required with Spring Boot, as it comes with support for embedded web servers. 
  • Helps to quickly and easily get started with microservices development.  Spring Boot has seen great adoption in recent times thanks to the advent of micro-services architecture style apps. Spring Boot supports creation of micro-services in the form of a JAR file, which could easily be deployed within a server container.
  • Supports features such as health checks, metrics, and so on.
  • Provides useful annotations such as @ConfigurationProperties to accomplish tasks such as loading properties' details from the application.properties file.

Building  Hello World web app using Spring Boot

In this section, we will go through the steps required to build a Hello World Web App using Spring Boot. The following given steps assume that you have set up Spring STS within your Eclipse IDE by following the steps given earlier. Now, with the steps given next, one can set up the Hello World web app in an easy manner:

  1. Press Ctrl + N to open up the Project creation Wizard dialog box.
  2. Write Spring in the Wizards text field. This will show various project options related to Spring projects. Select the option Spring Starter Project, and click on Next.
  1. Name the project HelloWorld, or leave the default name demo, and click on Next:
  1. Select Web in the list of dependencies as shown in the following screenshot, and click on Finish:
  1. Clicking on Finish will create a HelloWorld project whose file structure will look as seen in the following screenshot. Pay attention to the annotation @SpringBootApplication in the HelloWorldApplication class shown in the screenshot. Spring applications are commonly found to be annotated with annotations such as @Configuration, @EnableAutoConfiguration, and @ComponentScan. Spring Boot came up with @SpringBootApplication as an alternative to using three annotations together:
  1. Right-click on the project, and start Spring Boot App, as shown in the following screenshot. It will start the app on the embedded Tomcat server. Access the URL http://localhost:8080 on your browser. It will show up a page with the heading as Whitelabel Error Page followed by some content.
  1. At times, the project may not run successfully due to one or more errors encountered during setting up the project. One can open the Problems View, and find out if there are any issues. As creating and running a Maven project requires the dependencies to be downloaded from the internet, it is to be ensured that the internet connection is active while setting up and running the project.
  2. It is time to write some custom code in the HelloWorldApplication Java file, and run the app. Place the following code (in bold) as shown and run the app. Access the URL http://localhost:8080, and you will find the following getting printed: Hello World. How are you?. Pay attention to the usage of the @Controller annotation, which is required to annotate a class as a controller. In case one wants the class to serve REST APIs, one can use either @Controller and @ResponseBody together appropriately at the class and method level, or use @RestController at the class level:
        @Controller
@SpringBootApplication
        public class HelloWorldApplication {

@RequestMapping("/")
            String home() { 
                return "Hello world. How are you?";
            }

            public static void main(String[] args) {
                SpringApplication.run(HelloWorldApplication.class, args);
            }
        }
  1. When wanting to use the JSP files for displaying views, the following steps needed to be taken:
  • Include the following code in the pom.xml file. This is done to enable JSP support:
            <dependency>
              <groupId>org.apache.tomcat.embed</groupId>
              <artifactId>tomcat-embed-jasper</artifactId>
            </dependency>
            <dependency>
              <groupId>javax.servlet</groupId>
              <artifactId>jstl</artifactId>
            </dependency>
  • The following needs to be put in src/main/resources/application.properties in order to define the template prefix and suffix for the JSP files:
            spring.mvc.view.prefix=/WEB-INF/jsp/
            spring.mvc.view.suffix=.jsp
  • Create the JSP files in the folder src/main/resources/META-INF/resources/WEB-INF/jsp/. For the sake of this current example, let's call the file as index.jsp. 
  • The code for @Controller would look like the following to render the index.jsp file. Note the absence of the @ResponseBody annotation on the home() method. This is why the JSP file, index.jsp, is rendered. Otherwise, a string object would have been returned as a response leading to errors while rendering.

 

            @Controller
@SpringBootApplication
            public class HelloWorldApplication {

@RequestMapping("/")
                String home() { 
                    return "index";
                }

                public static void main(String[] args) {
                    SpringApplication.run(HelloWorldApplication.class, args);
                }
            }
 

Implementing Controllers


Before we go into the details of understanding the different aspects of implementing the controllers, let's quickly go over how controllers take part in the MVC workflow. 

The following is the workflow representing the processing of a web request and the response by a controller:

  1. The requests are first intercepted by the Dispatcher servlet.
  1. The Dispatcher servlet does the initial stage of request processing by resolving locale/themes, performing activities such as taking snapshots of the request attributes, and making framework objects available to handlers and view objects.
  2. Once the initial processing is done as mentioned in the preceding step, an appropriate handler is determined and invoked for further processing the request. It is the handleRequest method that is invoked on the handler instance, which, in turn, invokes the appropriate method on the appropriate controller determined using HandlerMappings.
  3. The controller then processes the request, and returns an instance of ModelAndView appropriately.
  4. The instance of ModelAndView is further processed by the Dispatcher servlet to send out the response to the user. 

Let us start building the sample healthcare app for doctors and patients. This app will be used to illustrate the controllers' implementations with multiple examples. In this section, we will see how to implement the controllers for handling users' requests for accessing the home page, signup form, login form, and so on. In the previous section, we have seen how to build a Hello World web app using Spring Boot. We will start implementing controllers using the Spring Boot app.

The following are the different controllers which are used to process requests for accessing the home page, signup form, and login page:

  • UserAccountController
  • RxController
  • DoctorSearchController

We will see the implementation of the preceding controllers in code examples listed as follows. In the most trivial form, controller classes can be implemented in the following manner:

  • The controller class can be annotated with @Controller or @RestController annotations at the class level. The code samples given below represent the usage of both types of annotations. @RestController is used as a convenience annotation to represent the annotations such as @Controller and @ResponseBody. When used at the class level, the controller can serve REST API requests.
  • When using the @Controller or @RestController annotation, request mappings can be provided using one of the following techniques:
    • Using RequestMapping Annotation at Method Level: The following code represents the RequestMapping annotation at the method level, home(). Spring framework 4.3 introduced annotations such as @GetMapping,@PostMapping,@PutMapping, and so on to simplify mappings for common HTTP method types such as GET, POST, PUT, and so on respectively. These new annotations enhance code readability. In the following code, the aforementioned annotations have been used interchangeably for providing greater clarity and ease of understanding. When the application is started, accessing http://localhost:8080/ would lead to a display of the view, index.jsp file. Note the controller class HealthApplication.
            @Controller
            @SpringBootApplication
            public class HealthApplication {

@RequestMapping("/")
                String home() { 
                    return "index";
                }

                public static void main(String[] args) {
                    SpringApplication.run(HelloWorldApplication.class, args);
                }
            }
    • In the code given next, the @GetMapping annotation is used in place of @RequestMapping, as shown in the preceding code sample. GetMapping is a composed annotation which acts as a shortcut for @RequestMapping (method = RequestMethod.GET). When the application is started, accessing http://localhost:8080/ will print Hello world. This is a health application!.

            @RestController
            @SpringBootApplication
            public class HealthApplication {

@GetMapping("/")
String home() { 
                    return "Hello world. This is a health application!";
                }

                public static void main(String[] args) {
                    SpringApplication.run(HelloWorldApplication.class, args);
                }
            }
  • Using RequestMapping Annotation at both, Method & Class Level: The following code represents RequestMapping at both the class and the method level. When the URL such as  http://localhost:8080/account/ is accessed, one will be taken to the login page. Note that a URL such as http://localhost:8080/account (without trailing "/") would result in an error. The point to note is that the login method does not have URL mapping with the RequestMapping annotation. This, primarily, acts as a catch--all method to handle all the requests with different paths represented by using "/*" (defined at the class level) except /account/.  When a URL such as http://localhost:8080/account/signup is accessed, the signup page is displayed. Similar is the case with the URL http://localhost:8080/account/forgotpassword which would open up the forgot password page.
        @Controller
        @RequestMapping("/account/*")
        public class UserAccountController {

@RequestMapping
         public String login() {
          return "login";
         }

@GetMapping("/signup")
         public String signup() {
          return "signup";
         } 

@GetMapping("/forgotpassword")
         public String forgotpassword() {
          return "forgotpassword";
         }
        }
  • Using RequestMapping Annotations with Http Requests Type: In the following example, the HTTP request type Get is mapped to the method login:
        @Controller
        @RequestMapping("/account/login")
        public class LoginController {
         //
         // @GetMapping can as well be used
         //
@RequestMapping(method = RequestMethod.GET)
         public String login() { 
          return "login";
         }
        }

In the next section, we will learn the concepts and examples of handling request parameters in relation to handling forms such as signup, login, and so on as discussed in this section.

 

Handling request parameters


There are different ways in which user request parameters can be handled. We shall be taking the example of the signup form to understand the following concepts related to the most common ways of handling request parameters:

  • Using the RequestParam annotation: This is used to bind the method parameters to web request parameters
  • Using the RequestBody annotation: This is used to bind the method parameter to the body of the web request
  • Using the PathVariable annotation: This is used to bind the method parameter to the URI template variable

The RequestParam annotation

In this section, we will learn how to use the RequestParam annotation for reading web request parameters in the controller class. The following image shows what the signup form looks like with three input fields such as Nick Name, Email address, and Password: 

Figure: New User Signup form

On submission of the preceding form, the user request parameters will be handled using the @RequestParam annotation. The RequestParam annotation is used to bind a method parameter to a web request parameter. The following code displays the binding of method parameters such as nickname, emailAddress, and password with web request parameters such as nickname, emailaddress, and password respectively. In simple words, the frontend has to send parameters with keys as nickname, email address, and password for the code given next to work. 

    @Controller
    @RequestMapping("/account/*")
    public class UserAccountController {

@GetMapping("/signup")
        public String signup() {
            return "signup";
        } 

@PostMapping("/signup/process")
        public String processSignup(ModelMap model, 
@RequestParam("nickname") String nickname, 
        @RequestParam("emailaddress") String emailAddress, 
        @RequestParam("password") String password) {
            model.addAttribute("login", true);
            model.addAttribute("nickname", nickname);
            return "index";
        }
    }

The RequestBody annotation

In this section, we will learn when and how to use the RequestBody annotation (@RequestBody) for handling web requests.

The RequestBody annotation is used to bind the method parameter with the body of the incoming request. In the process of binding, HttpMessageConverters converts the body of the request appropriately (most commonly into the parameter object) based on the content type of the request.

The RequestBody annotation is most commonly used in scenarios dealing with REST APIs. 

The following example demonstrates the usage of the @RequestBody annotation using a domain object, User, which is made a parameter to the controller method:

    @RestController
    public class RestDemoController {

@PostMapping("/hello")
        public HelloMessage getHelloMessage(@RequestBodyUser user) {
            HelloMessage helloMessage = new HelloMessage();
            String name = user.getName();
            helloMessage.setMessage( "Hello " + name + "! How are you doing?");
            helloMessage.setName(name);
            return helloMessage;
        }  
    }

In the preceding example, note some of the following:

  • The @PostMapping annotation maps the REST API endpoint, /hello, with the handler method, getHelloMessage. Recall that @PostMapping is a composed annotation which acts as a shortcut for @RequestMapping (method = RequestMethod.POST).
  • The @RequestBody annotation is used with the User object. This binds (or maps) the method parameter, user of type User, with the body of the web request. The body of the request arrives in the following JSON format:
        {"name": "Calvin Hobbes"}

The HttpMessageConverter method converts the preceding into the User object, whose code looks like the following:

    public class User {
      private String name;

      public String getName() {
        return name;
      } 

      public void setName(String name) {
        this.name = name;
      }
    }
  • The @RestController annotation, a convenient annotation, which is itself annotated with @Controller and @ResponseBody annotations, and is used for programming REST API integration endpoints.
  • The HelloMessage class is returned as a response. The following is the code for HelloMessage:
   public class HelloMessage {
     private String message;
     private String name;
     public String getMessage() {
       return message;
     }
     public void setMessage(String message) {
       this.message = message;
     }
     public String getName() {
       return name;
     }
     public void setName(String name) {
       this.name = name;
     }
   }

The HttpMessageConverter method converts the preceding response object into the following response message:

    {"message": "message text goes here...", "name": "name goes here..."}

The PathVariable annotation

In this section, we will learn how to use the PathVariable annotation for handling request parameters.

The PathVariable annotation is used to bind a method parameter to a URI template variable. A URI template is a URI-like string containing one or more variables. For example, in  the following code, /{nickname} is a URI template consisting of nickname as a variable. A method can have multiple @Pathvariable annotations to bind multiple variables present in the URI template. PathVariable is seen to be mainly used in the REST web service. Later, while going through Angular chapters, you will see that PathVariable is very similar to handling routing parameters using the ActivatedRoute concept:

    @Controller
    @SpringBootApplication
    public class HealthAppApplication {

      @RequestMapping("/") 
      public String home() {
        return "index";
      }

@GetMapping("/{nickname}")
      public String home(ModelMap model, @PathVariable String nickname) {
        model.addAttribute("name", nickname);
        return "index";
      }

      public static void main(String[] args) {
        SpringApplication.run(HealthAppApplication.class, args);
      }
    }

Both RequestParam and PathVariable can be used to read the request parameters. @RequestParam is used to retrieve the query parameters from the request URL. On the other hand, @PathVariable is used to retrieve one or more placeholders from the URI. URLs without query parameters, for example, paths, tend to be cached. The following code demonstrates the usage of both RequestParam and PathVariable. Different URLs will be handled using different methods represented in the code as follows:

  • URL (http://localhost:8080, localhost:8080/?name=calvin): This URL consists of a parameter such as name. The value of this parameter is obtained using RequestParam. Note that as required = false is declared with the @RequestParam definition in the code example given next, thus, a request URL such as http://localhost:8080/ would also get mapped to the usingRequestParam method. 
  • URI (http://localhost:8080/calvin): This URL consists of a name which can be handled using a placeholder variable whose value can be obtained using the PathVariable method.

The following code displays the usage of both RequestParam and PathVariable:

    @Controller
    @SpringBootApplication
    public class HealthAppApplication {
     //
     // RequestParam is used to retrieve value of parameter, name. 
     //
     @RequestMapping("/") 
     public String usingRequestParam(Model model, 
                   @RequestParam(value="name", required=false) String nickname) {
      model.addAttribute("nickname", nickname);
      return "index";
    }

    @RequestMapping("/{nickname}") 
     public String usingPathVariable(Model model, @PathVariable String nickname) 
     {
       model.addAttribute("nickname", nickname);
       return "index";
     }
   }

In the next section, you will learn how to use interceptors to handle web requests-response, before and after the requests are handled by the controller respectively.

 

Handling Interceptors


In this section, we will learn about some of the following:

  • Why use interceptors?
  • How to implement interceptor methods such as pre-handle and post-handle to process the web requests before and after the requests get processed by the controllers respectively. 

Interceptors' implementation provides methods for intercepting incoming requests before it gets processed by the controller classes or, intercepting the outgoing response after being processed by the controller and before being fed to the client. Interceptor methods help to get away with boilerplate code which is required to be invoked on each request and response. For example, let's take the authentication scenario where every request need to be checked for an authenticated session before being processed by the code in the controller. If the session is not found to be authenticated, the request is forwarded to the login page, else the request is forwarded to the controller for further processing. Given that the controller logic could span across multiple controller classes, and the aforementioned authentication logic needs to be processed before one or more controllers processes the incoming requests, it is suitable to put the authentication processing in the interceptor method. Another example of using interceptor methods includes measuring the time spent in the method execution.

For implementing interceptors for processing web requests-response, custom interceptor classes need to be implemented. Custom interceptor classes need to implement one or all of the methods provided in the HandlerInterceptor interface which are as follows:

  • preHandle: The code within the preHandle method gets executed before the controller method is invoked
  • postHandle: The code within the postHandle method is executed after the controller method is invoked
  • afterCompletion: The code within afterCompletion is executed after the view gets rendered 

In the following example, we will use an interceptor to process web requests arriving from the signup form. The following steps are required to be taken:

  • Create an Interceptor class: Create an Interceptor class extending the HandlerInterceptorAdapter class. In the following example, only the preHandle method is implemented. Other methods such as postHandle and afterCompletion are not provided with the implementation:
        public class SignupInterceptor extends HandlerInterceptorAdapter {

        @Override
        public boolean preHandle(HttpServletRequest request, 
               HttpServletResponse response, Object handler) throws Exception {

          String emailAddress = request.getParameter("emailaddress");
          String password = request.getParameter("password");

          if(StringUtils.isEmpty(emailAddress) ||             
          StringUtils.containsWhitespace(emailAddress) ||
          StringUtils.isEmpty(password) || 
          StringUtils.containsWhitespace(password)) {
            throw new Exception("Invalid Email Address or Password. 
                                 Please try again.");
          }

          return true;
        }

        @Override
        public void afterCompletion(HttpServletRequest request, 
                                    HttpServletResponse response, 
                                    Object handler, Exception exception)
        throws Exception {
          ... 
        }

        @Override
        public void postHandle(HttpServletRequest request, 
                    HttpServletResponse response, 
                    Object handler, ModelAndView modelAndView)
        throws Exception {
          ... 
        }
      }
  • Implement the Configuration class: Implement a custom @Configuration class by extending WebMvcConfigurerAdapter, thereby adding the interceptor with appropriate path patterns within the addInterceptors method. If the path pattern is not specified, the interceptor is executed for all the requests.
    @Configuration
    public class AppConfig extends WebMvcConfigurerAdapter { 
    
     @Override
     public void addInterceptors(InterceptorRegistry registry) {
     registry.addInterceptor(new SignupInterceptor()).addPathPatterns("/account/signup/process");
     }
    }
  • Make sure that the controller with the appropriate URI pattern is implemented as specified within the addInterceptors method in the aforementioned @Configuration class.
    @Controller
    @RequestMapping("/account/*")
    public class UserAccountController {
    
    @RequestMapping("/signup")
     public String signup() {
     return "signup";
     } 
    
    @RequestMapping("/signup/process")
     public String processSignup(ModelMap model, @RequestParam("nickname") String nickname,
     @RequestParam("emailaddress") String emailAddress, @RequestParam("password") String password) {
      model.addAttribute("login", true);
      model.addAttribute("nickname", nickname);
      return "index";
     }
    
    }

In the next section, you will learn about the different types of responses from controllers.

 

Handling Response


The following are some of the common types of responses returned from controllers:

  • An instance of ModelAndView 
  • Using @ResponseBody

Response as an instance of ModelAndView

ModelAndView is a container object to hold both Model and View. With ModelAndView as a return object, the controller returns the both model and view as a single return value. The model is a map object which makes it possible to store key-value pairs. The following code sample represents the usage of ModelAndView in a Controller:

    @Controller
    @RequestMapping("/account/*")
    public class UserAccountController {

      @PostMapping("/signup/process")
      public ModelAndView processSignup(ModelMap model, @RequestParam("nickname")       String  nickname, @RequestParam("emailaddress") 
      String emailAddress, @RequestParam("password") String password) {
        model.addAttribute("login", true);
        model.addAttribute("nickname", nickname);
        model.addAttribute("message", "Have a great day ahead.");
return new ModelAndView("index", model);
      }
    }

The following code samples represent the different ways in which an instance of ModelAndView is returned with different sets of information:

    // Will result in display of index.jsp page
    return new ModelAndView("index");

    // Will result in display of index.jsp page. 
    //The JSP page could consist of code such as "Hello ${name}" 
    //which will get displayed as "Hello Calvin Hobbes"  

    return new ModelAndView("index", "name", "Calvin Hobbes");

    // Will result in display of index.jsp page. 
    // The JSP page could consist of code such as 
    //"Hello ${model.firstName} ${model.lastName}" 
    //which will get displayed as "Hello Calvin Hobbes"

    UserInfo userInfo = new UserInfo();
    userInfo.setFirstName("Calvin");
    userInfo.setLastName("Hobbes");
    return new ModelAndView("index", "model", userInfo);

    // Will result in display of index.jsp page. 
    // The JSP page could consist of code such as "Hello ${name}" 
    // which will get displayed as "Hello Calvin Hobbes"

    Map<String, Object> map = new HashMap<String, Object>();
    map.put("name", "Calvin Hobbes");
    return new ModelAndView("index", map);

Using @ResponseBody annotation

This section represents the concepts related to the usage of the @ResponseBody annotation for returning a response to the client request.

The @ResponseBody annotation can be applied both at the class level and the method level. When @ResponseBody is applied at the class level along with the @Controller annotation, another annotation such as @RestController can be used instead.

The @ResonseBody annotation represents the fact that the value returned by the method will form the body of the response. When the value returned is an object, the object is converted into an appropriate JSON or XML format by HttpMessageConverters. The format is decided based on the value of  the produce attribute of the @RequestMapping annotation, and also the type of content that the client accepts. Take a look at the following example:

    @Controller
    public class RestDemoController {

      @RequestMapping(value="/hello", method=RequestMethod.POST, produces="application/json")
@ResponseBody
      public HelloMessage getHelloMessage(@RequestBody User user) {
        HelloMessage helloMessage = new HelloMessage();
        String name = user.getName();
        helloMessage.setMessage( "Hello " + name + "! How are you doing?");
        helloMessage.setName(name);
        return helloMessage;
      }
    }
 

Creating a RESTful web service


In this section, you will learn how to create a RESTful web service. The concepts explained earlier in this chapter will be used.

The following are some of the key aspects of creating a RESTful web service. Let's take the example of retrieving the doctors' list based on the specialties, location, and so on.

  • Create a controller representing the RESTful endpoint: In the following code, note the usage of the @RestController annotation, which is used to represent the annotation @Controller and @ResponseBody. The controller has a method, searchDoctor, for handling the request represented using the URL such as /doctors?location=xxx&speciality=yyy. Note the @RequestMapping annotation and its attributes, especially, "produces", which signifies the fact that the output sent to the user will be in the JSON format.
    @RestController
    public class DoctorSearchController {

      @Autowired
      DoctorService docService;

      @RequestMapping(value="/doctors", method=RequestMethod.GET, 
                      produces="application/json")
      public DoctorList searchDoctor(
             @RequestParam(value="location", required=false) String location,
@RequestParam(value="speciality", required=false) String speciality) 
      {
          DoctorList docList = docService.find(location, speciality);
        return docList;
      }
    }

The following is how the DoctorService implementation may look like:

    @Service
    public class DoctorServiceImpl implements DoctorService {

        @Autowired 
        private DoctorDAO doctorDAO;

        @Override
        public List<Doctor> findByLocationAndSpeciality(String location, String 
      speciality) {
          return doctorDAO.findByLocationAndSpeciality(location, specialityCode);
        }
    }

The following is how the DoctorDAO implementation may look like:

    @Repository
    @Transactional
    public class DoctorDAOImpl implements DoctorDAO {

      private SessionFactory sessionFactory;

      @Autowired
      public DoctorDAOImpl(SessionFactory sessionFactory) {
        this.sessionFactory = sessionFactory;
      }

      @Override
      public List<Doctor> findByLocationAndSpeciality(String location, String speciality) {
        Session session = this.sessionFactory.getCurrentSession();
        TypedQuery<Doctor> query = session.getNamedQuery("findByLocationAndSpeciality");
        query.setParameter("location", location);
        query.setParameter("speciality", speciality);
        List<Doctor> doctors = query.getResultList();
        return doctors;
      }
    }
  • Create a RESTful API: For retrieving the list of doctors based on location and speciality, the URL could look like  http://localhost:8080/doctors?location=xxx&speciality=yyy.
  • Identify the method of processing incoming requests data: @RequestParam will be used to process the incoming requests data as mentioned in the preceding URL. In the previous code, note how @RequestParam is used for processing the value of both the location and the specialty parameter. The following code represents the same:
        public DoctorList searchDoctor(
        @RequestParam(value="location", required=false) 
        String location, 
        @RequestParam(value="specialty", required=false) 
        String speciality) {
            // Code goes here
        }
  • Create a class representing ResponseBody: The return value is the DoctorList object, which consists of a list of Doctors. The following code represents the DoctorList object which is a list of the Doctor object:
    // The class representing the list of Doctor; Returned as a response
    public class DoctorList {
      private List<Doctor> doctors;

      public DoctorInfo(List<Doctor> doctors) {
        this.setDoctors(doctors); 
      }
      public List<Doctor> getDoctors() {
        return doctors;
      }
      public void setDoctors(List<Doctor> doctors) {
        this.doctors = doctors;
      }
    }

The following represents the Doctor class which is returned as part of the response object:

    public class Doctor {

      private String id;
      private String firstName;
      private String lastName;
      private String specialityCode;

      public String getId() {
        return id;
      }
      public void setId(String id) {
        this.id = id;
      }
      public String getFirstName() {
        return firstName;
      }
      public void setFirstName(String firstName) {
        this.firstName = firstName;
      }
      public String getLastName() {
        return lastName;
      }
      public void setLastName(String lastName) {
        this.lastName = lastName;
      }
      public String getSpecialityCode() {
        return specialityCode;
      }
      public void setSpecialityCode(String specialityCode) {
        this.specialityCode = specialityCode;
      }
    }
  • Client response: The client receives the response body in the JSON format. The following is a sample response which is returned by the execution of the preceding code:
    [{
     "id": "doc1",
     "firstName": "Calvin",
     "lastName": "Hobbes",
     "specialityCode": "pediatrics"
    },
    {
      "id": "doc2",
      "firstName": "Susan",
      "lastName": "Storm",
      "specialityCode": "cardiology"
    }]
 

Dockerizing a Spring Boot application


In this section, you will learn how to Dockerize a Spring Boot application. The detailed introduction to Docker and the related aspects such as Docker images, containers, Dockerfile, and so on is provided in Chapter 2, Preparing Spring Web Development Environment. In case, you are new to Docker, it may be a good idea to get an understanding of Docker before going ahead with this section.

The following are some of the reasons why it may be a good idea to Dockerize a Spring Boot application:

  • Containerized Springboot App-- a Microservice: It aligns well with the cloud-native architecture style, which recommends containerizing a microservice (using Docker or other runtimes such as rkt), and managing these containers using container orchestration tools such as Kubernetes, Docker Swarm, and so on. It should be noted that Spring Boot can be used to create a microservice. Thus, one may need to Dockerize a Spring Boot microservice when creating a cloud-native app.
  • Quick Dev/QA environments: A containerized/Dockerized Spring Boot app is easy to commission or decommission. 
  • Continuous delivery made easy: It is easy to achieve a continuous delivery of containerized/Dockerized Spring Boot app in different environments such as QA, UAT, production. 

It should be noted that Docker can be used while working with both Maven and Gradle, details of which will be presented in the next chapter while understanding the aspects of build. The following are some of the key aspects of Dockerizing a Spring Boot app. The same instructions can be used to wrap Spring boot micro-services within Docker containers.

  • Dockerfile: The first step is to create the Dockerfile which will be used to build the Docker image. The following is the content of the Dockerfile. Save the Dockerfile as Dockerfile in the root folder.
    FROM frolvlad/alpine-oraclejdk8:slim
    VOLUME /tmp
    ADD target/demo-0.0.1-SNAPSHOT.jar app.jar
    RUN sh -c 'touch /app.jar'
    ENV JAVA_OPTS=""
    ENTRYPOINT [ "sh", "-c", "java $JAVA_OPTS -
                 Djava.security.egd=file:/dev/./urandom -jar /app.jar" ] 
  • Build and test the Spring Boot app:
  1. Go to the Spring Boot app root folder. Make sure that you saved Dockerfile in the root folder. Execute the following command to build the Docker image:
    docker built -t springboot-app:latest .

    // In case you are executing the above command from another folder
docker built -t springboot-app:latest -f path_to_dockerfile.
  • Execute the following command to start and access the container:
    // Start the container; Name of the container is sbapp
docker run -tid -p 8080:8080 --name sbapp springboot-app:latest

    // Access the container
docker exec -ti sbapp bash

    // Access the logs
docker logs sbapp

Once started, open the REST client, and test the preceding RESTful API with URL as http://localhost:8080/doctors?location=xxx&speciality=yyy.

 

Summary


In this chapter, you learnt the fundamentals of the Spring web framework with a focus on understanding the key concepts such as IOC container, Dispatcher servlet, implementing controllers, web requests and response handling, using custom interceptors, and so on. You also learnt how to create a web application using Spring Boot, how to create a RESTful web service, and finally, how to Dockerize a Spring Boot application.

In the next chapter, you will learn how to prepare a Spring web development environment.

About the Author

  • Ajitesh Shukla

    Ajitesh Shukla is an accomplished software engineer with over 18 years experience in the IT industry, taking up different roles and responsibilities in startups and bigger companies, including Infosys, Computer Associates, WaveCrest, Evoke Technologies, and Raksan Consulting. He has worked on several web and mobile app projects based on technologies such as Java, Spring, Hibernate, AngularJS/Angular, ReactJS, and Ionic framework. Currently, he's interested in building cloud-native apps, cloud platforms, blockchain, and cyber security.

    Ajitesh is an avid blogger and writes for websites such as DZone and Vitalflux, among many others. His hobbies include playing musical instruments, writing, and attending and organizing meetups.

    Currently, Ajitesh has been working with a startup, Raksan consulting, as a Technology Evangelist where he is leading their innovation team and carving out newer solutions in the area of cloud platforms such as AWS/Google cloud, cloud-native technologies, data analytics, and blockchain.

    You can follow Ajitesh on LinkedIn (/ajitesh) and GitHub (/eajitesh). You can also follow Ajitesh on Stack Overflow (/users/554132/ajitesh).

    Browse publications by this author

Latest Reviews

(8 reviews total)
sono trattate tutte le funzionalità ma, secondo me, adatto per che conosce l'argomento e vuole testare le conoscenze già in acquisite.
Poorly written. Needs an experienced editor....
Topics aren't covered in depth

Recommended For You

Hands-On Microservices with Spring Boot and Spring Cloud

Apply microservices patterns to build resilient and scalable distributed systems

By Magnus Larsson
Spring 5.0 Projects

Discover the latest features of Spring framework by building robust, fast, and reactive web applications

By Nilang Patel
Spring Boot 2.0 Projects

Develop diverse real-life projects including most aspects of Spring Boot

By Mohamed Shazin Sadakath