Chapter 15. mvc

15.1. Introduction

In this lab you will implement basic Spring MVC Controllers to invoke application functionality and display results to the user.

What you will learn:

  1. How to set up required Spring MVC infrastructure
  2. How to expose Controllers as endpoints mapped to web application URLs

Specific subjects you will gain experience with:

  1. Spring Boot for Web
  2. @Controller
  3. Configuring a ViewResolver

Estimated time to complete: 30 minutes

15.2. Quick Instructions

Quick instructions for this exercise have been embedded within the lab materials in the form of TODO comments. To display them, open the Tasks view (Window >> Show View >> Tasks (not Task List)).

15.3. Detailed Instructions

The instructions for this lab are organized into three main sections.

  1. Write a simple "Hello" controller, reviewing the dependencies required.
  2. A specification of the Accounts web application functionality you will implement in this lab.
  3. Implement the required web application functionality.

15.3.1. Part 1: Writing a Simple Controller

This project is already setup as a Spring Boot application - check the pom.xml to see the Spring Boot dependencies being used (TODO-01). Do not modify the POM.

Run this as a Spring Boot application using Run As …​ Spring Boot App (on STS) or as a Java application on any other IDE (TODO-02).

Go to http://localhost:8080 and you should see a simple home page. The original HTML is in src/main/resources/templates/index.html.

[Note] Note

If your application deploys successfully there should be a lot of logging output in the console window and the images on the page should appear correctly.

If you click on either link you will get the Spring Boot "Whitelabel Error Page" because neither exists (status-404).

Open HelloController.java and you will see the home() method that allowed the home-page to work - it simply returned classpath:/templates/index.html and since this is a maven derived project, that translates to src/main/resources/templates/index.html.

Add a new method (follow the instructions in TODO-03) to implement the welcome page - which already exists at src/main/resources/templates/welcome.html. It expects a {{name}} attribute.

Once the application restarts, return to the home page and see if the Hello page link works. You should see a minimal page containing the text "Hello from …​".

[Note] Note

The application includes Spring Boot Developer Tools which will automatically restart the application if any file in any of the Maven source directories is changed.

Time to do something more challenging.

15.3.2. Part 2: Setting up the Spring MVC infrastructure

15.3.2.1. The Requirement

The Accounts application we are developing should allow users to see a list of all accounts in the system, then view details about a particular account. This desired functionality is shown below:

accountList

Figure 15.1. GET /accounts/accountList



You should see a listing of all accounts by name with links to view details. Once implemented, clicking on a link will take you to the account details page as shown below (however this is not yet implemented).

accountDetails

Figure 15.2. Show Details for Account '0'



15.3.2.2. Running the application

First we need to activate the AccountController which is partly written for you but Spring MVC currently doesn’t recognize it. (TODO-05) Modify the AccountController as required.

Once the server restarts, return to the home page, http://localhost:8080, and click on the List Accounts link. You should see a list of accounts display successfully. This 'accountList' functionality has been pre-implemented for you. We will review and change some of that in a moment, but it at least gets you started with the functionality.

Now try clicking on one of the account links. You will get a 404 error page indicating there is no handler for this request because the 'accountDetails' functionality has not yet been implemented.

It is good practice to Unit test your classes and the AccountController is no exception. Go to the AccountControllerTests, review the only test in there, testHandleListRequest() - try running it (TODO-06). It should pass.

15.3.2.3. Review the Existing Application Code

Because this is a Spring Boot application and Spring Web and Spring MVC are on the classpath, the Dispatcher Servlet and a default Spring MVC setup has been configured, allowing our controllers to work.

If you look in AccountsApplication.java you will see:

  1. It uses @EntityScan to find our JPA domain objects.
  2. It imports RootConfig to define our application and other beans

Open RootConfig.java. What does it do?

  1. Imports the application and database configurations (from rewards-db project).
  2. Defines the new accountManager bean - the key service that we are using to load accounts from the database for display.
  3. Defines a default MustacheViewResolver so our template views work.

Finally, review the Java implementation of the AccountController to see how it works:

  1. Notice how the @RequestMapping annotation ties the /accountList URL to the accountList() method
  2. That this method delegates to the AccountManager to load a list of Accounts
  3. This list is added to the model for display to the user
  4. Finally it requests the accountList.html template view to render the list by returning its location as a String
  5. The full classpath location of the template is required, the MustacheViewResolver hasn’t been configured with their location
  6. Ultimately this tells the DispatcherServlet to use this view to render the model

15.3.2.4. Reviewing the Whole System

Lets quickly summarize the big picture. What happened when you clicked on the Account List link?

The browser issued a GET request to http://localhost:8080/accountList which set the following steps in motion:

  1. The request was first received by the embedded Tomcat Servlet Engine, which routed it to the DispatcherServlet.
  2. The DispatcherServlet then invoked the accountList() method on the AccountController based on the @RequestMapping annotation.
  3. Next, the AccountController loaded the account list and selected the "accountList.html" view.
  4. Finally, the accountList.html rendered the response which you see before you (actually the Mustache Template processor did the rendering).

At this point you should have a good feel for how everything works so far. You should also have an idea of how to add the remaining "accountDetails" functionality to this application. You simply need to define a new method encapsulating this functionality, test it, and map it to the appropriate URL. You’ll do that soon.

But first let’s get rid of those absolute paths to views.

15.3.2.5. Setup the View Resolver

The view name in our handler method uses an absolute path. this means the method is aware of the specific type and location of the views that will be rendered (HTML templates in this case). They are also long and tedious to use.

Spring recommends decoupling request handling from response rendering details. In this step, you will add a ViewResolver to provide a level of indirection.

(TODO-07a - TODO-7d) Do the following . Modify AccountController.accountList to return the logical view name accountList . Comment out the MustacheViewResolver @Bean method in RootConfig . Get Spring Boot to set it up by specifying the necessary configuration properties in application.properties . Modify the methods in HelloController to return just the logical view name

Once the application has restarted, navigate through the web-site and ensure it all still works. If you are still able to view the home page, hello page and the list of accounts, your changes are correct.

Strictly speaking we should have tested this first, so return to the AccountControllerTests (TODO-08) and refactor so that it expects the logical view name. Rerun the test, it should work again.

15.4. Step 3: Implementing another Spring MVC handler method

Now you will implement the handler method that supports the functionality for the missing account details page. Once you have completed this section, you will no longer get a 404 when you click on an account link from the account list view. Instead, you will see the details of that account.

15.4.1. Implement the account details request handler

(TODO-09) In the AccountController, add a method to handle requests for account details. The method should use the account identifier passed with the HTTP request to load the account, add it to the model, and then select a view. What attribute name will you use for the account attribute when you add it to the model?

[Tip] Tip

In your web browser, try clicking on an account and look at the URL to see which parameter name is used to pass in the account identifier. Ignore the 404 error.

The Mustache template has already been implemented for you. Review it in the src/main/resources/templates directory to see what attribute name it is expecting - it is the variable in {{ }}.

15.4.1.1. Testing the controller

We’re almost done! There are two things we still have to do. First of all, we have to test the controller.

(TODO-10) Open AccountControllerTests and review how the accountList() method has been tested. As you can see, it just calls the handler method (without having to worry about doing any web setup) and inspects if the model has been correctly filled. In this step, we will do the same for the accountDetails() method.

Implement a method called testHandleDetailsRequest() to test the controller - in a similar way to testHandleListRequest(). There should be one attribute in the model. What is its name? What type is it? Get the attribute and confirm it contains the right data. Don’t forget to annotate the new method with @Test.

[Note] Note

The ability to test Spring MVC Controllers out-of-the-container is a useful feature. Strive to create a test for each controller in your application. You’ll find it proves more productive to test your controller logic using automated unit tests, than to rely solely on manual testing within your web browser.

When all tests pass, carry on.

15.4.2. Running the application

Finally (TODO-11) try to run the web application again and make sure the functionality you implemented works. If it doesn’t, try to find where you might have gone wrong and possibly talk to your instructor.

15.4.3. EXTRA CREDIT: Mock MVC Testing

If there is time left, you can try this optional section.

Spring’s Mock MVC testing framework allows a JUnit test to drive Spring MVC as if it was running in a container - enabling more powerful testing that the simple AccountControllerTests you have been using.

Open the class MockMvcTests and follow the TODO steps. Most of the code has been written for you. The important part is to see if you understand how the tests work - we did not cover Mock MVC testing in the course notes.

For more information, refer to the online documentation.