Working with Controllers

Controllers do all the work to process the request, build the model based on the request, and pass the model to the view for rendering. Spring's DispatcherServlet intercepts the requests from the client and uses a HandlerAdapter implementation that is responsible for delegating the request for further processing. You can implement the HandlerAdapter yourself, which allows you to modify the chain of command the request must pass through.

The DispatcherServlet has a List handlerAdapters property that allows you to specify the HandlerAdapters you wish to use. To make sure the HandlerAdapter implementations are called in the right order, you can choose to implement the Ordered interface in your HandlerAdapter to indicate its position of among other HandlerAdapters.

If the handlerAdapters property of DispatcherServlet is null, the DispatcherServlet uses SimpleControllerHandlerAdapter. Because we are not going to provide any additional HandlerAdapter implementations, our application is going to use the SimpleControllerHandlerAdapter.

Because the SimpleControllerHandlerAdapter's handle() method calls ((Controller) handler).handleRequest(request, response), the Spring handler beans that are to act as controllers must implement the Controller interface. This approach makes it very easy to write your own implementation from scratch or to use one of the convenience superclasses. The Controller interface depends on HttpServletRequest and HttpServletResponse, which means that you can use it only in web applications.

Let's take a look at the most basic implementation of the Controller interface. In Listing 17-4, we create an IndexController that simply writes "Hello, World" to the response stream.

Listing 17-4: IndexController Implementation

image from book
package com.apress.prospring.ch17.web;      import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;      import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller;      public class IndexController implements Controller {          public ModelAndView handleRequest(HttpServletRequest request,          HttpServletResponse response) throws Exception {                  response.getWriter().println("Hello, world");                  return null;     }      }
image from book

The only method we need to implement is ModelAndView handleRequest(HttpServletRequest, HttpServletResponse). We return null as ModelAndView, which means that no view is rendered and the output written to the output of the response is committed and returned to the client.

Implementing the Controller interface is not, in most cases, too much work because Spring provides a number of useful superclasses.

AbstractController

At the first glance, it might seem that AbstractController is simply a wrapper around the interface that forces you to implement the handleRequestInternal method to process the request. This is only partially true because AbstractController extends a WebContentGenerator class that allows you to set additional properties to control the request and response. Additionally, the WebContentGenerator extends WebApplicationObjectSupport, which, in turn, extends an ApplicationObjectSupport class that implements ApplicationContextAware. In other words, extending your controller from AbstractController rather than implementing the Controller interface directly gives you access to the ServletContext, WebApplicationContext, ApplicationContext, Log, and MessageSourceAccessor.

In Table 17-2, we examine the properties you can set that are related to the web application environment.

Table 17-2: WebContentGenerator/AbstractController Properties

Property

Description

Default Value

supportedMethods

Supported and allowed HTTP methods.

GET, POST

requiresSession

Specifies whether an HttpSession instance is required to process the request.

false

useExpiresHeader

Specifies whether to use the HTTP 1.0 expires header.

true

useCacheControlHeader

Specifies whether to use the HTTP 1.1 cache- control header.

true

cacheSeconds

Instructs the client to cache the generated content for the specified number of seconds.

–1

synchronizeOnSession

Specifies whether the controller should synchronize instances of HttpSession before invoking handleRequestInternal. This is useful for serializing reentrant request handling from the same client.

false

As an example, in Listing 17-5, we set the cacheSeconds property to 10 and then refresh the page in the client (making sure we are not instructing the client to bypass the cache). As a result, we should only see new content from the server every 10 seconds.

Listing 17-5: IndexController Implementation Using AbstractController

image from book
package com.apress.prospring.ch17.web;      import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;      import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.AbstractController;      public class IndexController extends AbstractController {          protected ModelAndView handleRequestInternal(HttpServletRequest request,          HttpServletResponse response) throws Exception {         setCacheSeconds(10);         response.getWriter().println("Hello, world at " +              System.currentTimeMillis());         return null;     }      }
image from book

If you compare the implementations of IndexController in Listing 17-4 and Listing 17-5, you will see that there is very little difference between them, except for the fact that the code in Listing 17-5 now has full access to the context (both servlet and application) and can manipulate the HTTP headers more easily. In Listing 17-14 later in this chapter, we show you how to access the application context.

ParameterizableViewController

This is a ridiculously simple subclass of AbstractController; it implements the handleRequestInternal method to return a new model with a name set in its viewName property. No data is inserted into the model, and the only reason you would chose to use this controller is to simply display a view using its name.

Listing 17-6 shows the ParameterizableIndexController we created to demonstrate the functionality of ParameterizableViewController.

Listing 17-6: ParameterizableIndexController Implementation

image from book
package com.apress.prospring.ch17.web;      import org.springframework.web.servlet.mvc.ParameterizableViewController;      public class ParameterizableIndexController extends ParameterizableViewController {      }
image from book

In Listing 17-7, we add a parameterizableIndexController bean to the application context file and set its viewName property to product-index as well as add a reference to it to the publicUrlMapping bean.

Listing 17-7: parameterizableIndexController Bean Declarations

image from book
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" ¿ "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>      <bean           >         <property name="interceptors">             <list>                 <ref local="bigBrotherHandlerInterceptor"/>             </list>         </property>         <property name="mappings">             <props>                 <prop key="/index.html">indexController</prop>                 <prop key="/pindex.html">parameterizableIndexController</prop>                 <prop key="/product/index.html">productController</prop>                 <prop key="/product/view.html">productController</prop>                 <prop key="/product/edit.html">productFormController</prop>                 <prop key="/product/image.html">productImageFormController</prop>             </props>         </property>     </bean>          <bean bold">parameterizableIndexController"          >         <property name="viewName"><value>products-index</value></property>     </bean> </beans>
image from book

MultiActionController

This Controller implementation is far more interesting than ParameterizableViewController. It is also a subclass of AbstractController, which means it has access to all its properties and methods. Most importantly, it lets you provide as many implementations of public ModelAndView(HttpServletRequest, HttpServletResponse) throws Exception as you need. You can choose to implement the methods in your subclass of MultiActionController, or you can specify a delegate object that implements these methods. In the latter case, the MultiActionController invokes the methods on the delegate object.

The two additional properties to AbstractController, delegate and methodNameResolver, are used to tell MultiActionController which method on which object to invoke for each request. If the delegate property is left to its default value of null, the method is looked up and invoked on MultiActionController subclass; if delegate is not null, the method is invoked on delegate.

The methodNameResolver must be set to an implementation of MethodNameResolver. There are three implementations of MethodNameResolver, which are described in Table 17-3.

Table 17-3: MethodNameResolver Implementations

Implementation

Description

InternalPathMethodNameResolver

The method name is taken from the last ("file") part of the path, excluding the extension. When using this resolver path, /servlet/foo.html maps to a method declared as public ModelAndView foo(HttpServletRequest, HttpServletResponse). This is also the default implementation used in MultiActionController.

ParameterMethodNameResolver

The method name is taken from the specified request parameter. The default parameter name is action; you can change the parameter name in the context file.

PropertiesMethodNameResolver

The method name is resolved from an external properties file. You can specify exact mapping, such as /test.html=handleTest or you can use wildcards, such as /*=handleAllTs.

If none of the resolvers shown in Table 17-3 are suitable for your application, you can implement your own MethodNameResolver, but the resolvers already provided out of the box by Spring are usually sufficient. In Listing 17-8, we take a look at the simplest implementation of the MultiActionController subclass.

Listing 17-8: MultiActionController Subclass

image from book
package com.apress.prospring.ch17.web.product;      import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse;      import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController;          public class ProductController extends MultiActionController {          public ModelAndView view(HttpServletRequest request,          HttpServletResponse response) throws Exception{                  response.getOutputStream().print("Viewing product " +              request.getParameter("productId"));                  return null;     }      } 
image from book

The ProductController from Listing 17-8 adds only one method, view(). If the path /product/* is mapped to this controller, and if the request is

/product/view.html?productId=10, the output displayed in the browser is going to be Viewing product 10.

This proves that, by default, MultiActionController uses InternalPathMethodNameResolver as a methodNameResolver and that the delegate property is null, because the view() method is called on ProductController. Let's take a look at how we can configure other methodNameResolvers, starting with ParameterMethodNameResolver.

By default, ParameterMethodNameResolver uses action as the parameter name from which to get the method name; we can change (as we do in Listing 17-9) that by setting the paramName property. We can also specify the method name that is invoked when the paramName parameter is not present in the request by setting the defaultMethodName property to the name of the method to be invoked.

Listing 17-9: ch17-servlet.xml Definition with ParameterMethodNameResolver

image from book
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"     "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>     <!-- other beans -->     <bean           >         <property name="methodNameResolver">             <ref local="productMethodNameResolver"/></property>     </bean>          <bean bold">productMethodNameResolver"          bold">¿             ParameterMethodNameResolver">             <property name="paramName"><value>method</value></property>             <property name="defaultMethodName"><value>view</value></property>          </property>     </bean> </beans>
image from book

If we now make a request to /product/a.html and do not specify the method parameter, ProductController.view is invoked; we get the same behavior if we make a request to /product/ a.html?method=view. However, if we make a request to /product/a.html?method=foo, we get an error message because method public ModelAndView foo(HttpServletRequest, HttpServletResponse) is not implemented in ProductController.

The last method name resolver we discuss is PropertiesMethodNameResolver (see Listing 17-10). This method resolver relies on the request URI, but unlike InternalPathMethodNameResolver, we can specify the method names in the Spring context file.

Listing 17-10: ch17-servlet.xml Definition with PropertiesMethodNameResolver

image from book
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN"  "http://www.springframework.org/dtd/spring-beans.dtd">      <beans>     <!-- other beans -->     <bean           >         <property name="methodNameResolver">             <ref local="productMethodNameResolver"/></property>     </bean>          <bean bold">productMethodNameResolver"          bold">¿             PropertiesMethodNameResolver">         <property name="mappings">             <props>                 <prop key="/product/view.html">view</prop>                 <prop key="/product/v*.html">view</prop>             </props>         </property>     </bean> </beans>
image from book

This code demonstrates how to use PropertiesMethodNameResolver—we need to configure its mappings property and add a list of mappings and their handler methods. The example from Listing 17-10 declares that /product/view.html as well as /product/v*.html map to the public ModelAndView view(HttpServletRequest, HttpServletResponse) method in ProductController. The benefit of this MethodNameResolver is that we can use wildcards in the mapping strings.

All these controllers are very useful, but if we have to process input submitted by a user, we have to write a lot of code to get the submitted values and process error messages. Spring simplifies this process by providing several command controllers. Before we can move ahead to the command controllers, however, we must discuss Spring views. This enables us to create pages that the command controllers will use to process the data the users enter.



Pro Spring
Pro Spring
ISBN: 1590594614
EAN: 2147483647
Year: 2006
Pages: 189

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net