Richard’s Weblog

January 21, 2009

Spring-MVC from scratch, the Controller interface

Filed under: Spring Framework,Web development — Richard @ 4:51 pm
Tags: ,

Spring controllers and tools to use them are becomming more and more powerful.  This kind of power often comes with complexity and sometimes there is so much information to assimilate at the same time that it is difficult to know where to start.  This post comes from my personnal notes when trying to learn how to use Spring-mvc.  I’ll start by defining a simple use case, and then by implementing and testing the mvc part of the scenario.

So here is the use case :
I want a web page to display a search form.  When clicking on the search button, the system looks for a list of books whose title or author matches the search string entered in the form.  The same page must be used to display the search form and the search results.  Think of a blurred, scaled down form of Amazon.  The goal here is only to understand the basics of using and testing Spring controllers.  So to begin, we will do the implementation only using Spring’s Controller interface.

Let’s think about what the controller should do, and build a simple unit test to represent the expected behavior.  First, our controller will be able to process a request made with “GET” and “POST” http methods.  When queried with the “GET” method, the controller will forward to the form page (in this case, the form page is the same as the results page).  When queried with the “POST” method, the controller will know the search form has been submitted, and will manage to get a list of corresponding books.  It will then forward that list to the results page who will display information about each listed book.

Based on this scenario, I will first write a unit test describing what the controller is intended to do.  For this, I use mock objects (such as MockHttpServletRequest) from the Spring package org.springframework.mock.web included in the file spring-test.jar, distributed with the Spring Framework.  I have two behaviors to test : the “GET” request, asking for the search form, and the “POST” request, handling form submission and populating the model with a list of found books, allowing the choosen view to display these books.  Here our controller will be very basic : a class that implements Spring’s Controller interface.  This interface contains only one method, taking an HttpServletRequest and HttpServletResponse as parameters, and returning a ModelAndView object.  The ModelAndView class exists because a method can only return one object.  The controller is responsible for choosing which view will be returned to the user, and what that view has to display (the model, in our case : the books to display).  The ModelAndView object stands exactly for this : it contains information on the view to render after controller execution, if any, and it also contains all the information that view will have to display.  Here is the code to test my controller :

@Test
public void testGetForm() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    SearchController controller = new SearchController();

    request.setMethod("GET");
    ModelAndView mav = null;
    try {
        mav = controller.handleRequest(request, null);
    } catch(Exception e) {
        fail();
    }
    assertEquals("/book/searchForm.jsp", mav.getViewName());
}

@Test
public void testSubmitSearch() {
    MockHttpServletRequest request = new MockHttpServletRequest();
    request.setMethod("POST");
    request.setParameter("searchString", "Test-Driven Development");
    SearchController controller = new SearchController();
    ModelAndView mav = null;
    try {
    	 mav = controller.handleRequest(request, null);
    } catch(Exception e) {
    	fail();
    }
    assertNotNull(mav.getModelMap().get("books"));
    assertNotNull(mav.getModelMap().get("searchString"));
    assertEquals("/book/searchForm.jsp", mav.getViewName());
}

The two methods test the two possible ways to call our controller : to get the search page the first time, and to get results when submitting a search for books.  Note that when submitting a search, even if no book was found, an attribute called “books” must exists in the Model.

Now we have a test that seems to describe what we expect of our controller.  Let’s now code that controller :

public class SearchController implements Controller {
    public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
        ModelAndView mav = new ModelAndView("/book/searchForm.jsp");
        if("POST".equalsIgnoreCase(request.getMethod())) {
            handleFormSubmission(request, mav);
        }
        return mav;
    }
    private void (handleFormSubmission(HttpServletRequest request, ModelAndView mav) {
        String searchString = request.getParameter("searchString");
        mav.addObject("searchString", searchString);
        // if an empty search string have been entered, no need to go further.
        if(searchString == null || searchString.trim().length() == 0)
            return;

        List<Book> books = new LinkedList<Book>();
        // Fake we found some results.
        Book book = new Book();
        book.setTitle("Test Driven Development");
        book.setISBN("0-321-14653-0");
        book.setAuthor("Kent Beck");
        books.add(book);
        mav.addObject("books", books);
    }
}

The code has to check explicitly if it should perform a search or not, by looking at the HTTP method used to call the controller. Notice again the use of the ModelAndView object, specifying that the view called “/book/searchForm.jsp” will be used to render (display) the model.  This example uses Spring-mvc completely out of the box, with the default configuration that comes with it ( no explicit HandlerMapping nor ViewResolver defined ).  The only configuration elements are the DispatcherServlet configuration in web.xml :

<servlet>
    <servlet-name>core</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <load-on-startup>1</load-on-startup>
    <init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
    </init-param>
</servlet>
<servlet-mapping>
    <servlet-name>core</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

And my controller definition :

<bean name="/book/search" class="com.company.x.samples.bookbay.search.SearchController"/>

The model, for my example, only contains one attribute, and only when a search has been submitted (this attribute is the list of books returned by the search).

Let’s now write the view.  This view will be responsible for displaying the form and the search results.  To do this, it will need to access the “books” attribute our controller sets in the ModelAndView object.  When using Jsp with our views, Spring’s dispatcher servlet transfers model’s attributes to request attributes, enabling acces from the view via JSTL :

<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
    <body>
        <c:url var="searchControllerURL" value="/book/search"/>
        <form method="POST" action="${searchControllerURL}">
 	    <input type="text" name="searchString"/>
            <input type="submit" value="search"/>
        </form>

        <c:if test="${not empty requestScope.books}">
            Search results for "${requestScope.searchString}" :
<table>
            <c:forEach var="book" items="${requestScope.books}" varStatus="rowCounter">
                <c:if test="${rowConter.count eq 0}">
<tr>
<th>Title</th>
<th>Author</th>
<th>ISBN</th>
</tr>
</c:if>
<tr>
<td>${book.title}</td>
<td>${book.author}</td>
<td>${book.isbn}</td>
</tr>
</c:forEach></table>
</c:if>
    </body>
</html>

Note here that the form’s action attribute is pointing directly to the configured bean name for our controller.  This is the default Spring behavior since we did’nt configure any HandlerMapping.  Also note how we access model data via JSTL request scope, proving the dispatcher servlet mapped our model’s attributes to the request.

In a future post, I’d like to show how to modify this same example to use other Spring’s controllers, such as the SimpleFormController, and then compare the implementations.

Edit : since this post, I’ve continued the example using AbstractController template method implementation.

About these ads

8 Comments »

  1. Hi,

    Nice tutorial continue this good work.

    regards,
    VaanNila

    Comment by Eswar — July 8, 2009 @ 7:19 pm | Reply

  2. It is a very descriptive post. Thanks for sharing.

    Comment by Radsa — January 24, 2010 @ 9:51 pm | Reply

  3. Excellent tutorial! Excellent explanation of the concept. One thing that would be helpful for novices is to know what dependencies to place in WEB-INF\lib. There are a number of them!

    Comment by Eugene — March 11, 2010 @ 6:51 pm | Reply

  4. your tutorial is very nice

    Comment by annapurna — March 28, 2010 @ 1:49 am | Reply

  5. pappa ga entra adi..emi artham kaldu

    Comment by ar — October 5, 2010 @ 2:45 am | Reply

  6. 9T6nPA http://gdjI3b7VaWpU1m0dGpvjRrcu9Fk.com

    Comment by frenky — May 7, 2011 @ 7:52 pm | Reply

  7. hi good one satya

    Comment by satya — July 21, 2011 @ 5:08 am | Reply

  8. Spring-MVC from scratch, the Controller interface « Richard’s Weblog…

    Thank you for submitting this cool story – Trackback from Blue Ray Plus – Latest Technology News…

    Trackback by Blue Ray Plus - Latest Technology News — April 27, 2012 @ 7:08 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme. Create a free website or blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: