diff --git a/spring-framework-reference/src/mvc.xml b/spring-framework-reference/src/mvc.xml index 37609f7e64f..8ff7c2cf9ad 100644 --- a/spring-framework-reference/src/mvc.xml +++ b/spring-framework-reference/src/mvc.xml @@ -1,8 +1,7 @@ - - +"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> + Web MVC framework
@@ -11,25 +10,14 @@ Spring's Web MVC framework is designed around a DispatcherServlet that dispatches requests to handlers, with configurable handler mappings, view resolution, locale and - theme resolution as well as support for upload files. The default handler - is a very simple Controller interface, just - offering a ModelAndView handleRequest(request,response) - method. This can already be used for application controllers, but you will - prefer the included implementation hierarchy, consisting of, for example - AbstractController, - AbstractCommandController and - SimpleFormController. Application controllers will - typically be subclasses of those. Note that you can choose an appropriate - base class: if you don't have a form, you don't need a form controller. - This is a major difference to Struts. - - - Since Spring 2.5, an annotated controller style is available - for Java 5+ users. This is a compelling alternative to implementing - traditional Controller (sub-)classes, - allowing for flexible multi-action handling. See the - section for details. - + theme resolution as well as support for uploading files. The default + handler is based on the @Controller and + @RequestMapping annotations, offering a + wide range of flexible handling methods. With the introduction of Spring + 3.0, the @Controller mechanism also allows + your to create RESTful Web sites or application, though the + @PathVarariable annotation and other + features. <quote>Open for extension...</quote> @@ -66,42 +54,23 @@ Note that you cannot add advice to final methods using Spring MVC. This means it won't be possible to add advice to for example the AbstractController.handleRequest() method. Refer to - for more information on + for more information on AOP proxies and why you cannot add advice to final methods. Spring Web MVC allows you to use any object as a command or form - object - there is no need to implement a framework-specific interface or - base class. Spring's data binding is highly flexible: for example, it - treats type mismatches as validation errors that can be evaluated by the - application, not as system errors. All this means that you don't need to - duplicate your business objects' properties as simple, untyped strings in - your form objects just to be able to handle invalid submissions, or to - convert the Strings properly. Instead, it is often preferable to bind - directly to your business objects. This is another major difference to - Struts which is built around required base classes such as - Action and - ActionForm. - - Compared to WebWork, Spring has more differentiated object roles. It - supports the notion of a Controller, an - optional command or form object, and a model that gets passed to the view. - The model will normally include the command or form object but also - arbitrary reference data; instead, a WebWork - Action combines all those roles into one - single object. WebWork does allow you to use existing business objects as - part of your form, but only by making them bean properties of the - respective Action class. Finally, the same - Action instance that handles the request is - used for evaluation and form population in the view. Thus, reference data - needs to be modeled as bean properties of the - Action too. These are (arguably) too many - roles for one object. + backing object - there is no need to implement a framework-specific + interface or base class. Spring's data binding is highly flexible: for + example, it treats type mismatches as validation errors that can be + evaluated by the application, not as system errors. All this means that + you don't need to duplicate your business objects' properties as simple, + untyped strings in your form objects just to be able to handle invalid + submissions, or to convert the Strings properly. Instead, it is often + preferable to bind directly to your business objects. Spring's view resolution is extremely flexible. A - Controller implementation can even write a - view directly to the response (by returning null for - the ModelAndView). In the normal case, a + Controller implementation can even write + directly to the response stream. In the normal case, a ModelAndView instance consists of a view name and a model Map, which contains bean names and corresponding objects (like a command or form, containing reference data). @@ -152,8 +121,8 @@
Features of Spring Web MVC - - + Spring's web module provides a wealth of unique web support features, including: @@ -175,10 +144,10 @@ - Adaptability, non-intrusiveness. Use whatever controller - subclass you need (plain, command, form, wizard, multi-action, or a - custom one) for a given scenario instead of deriving from a single - controller for everything. + Adaptability, non-intrusiveness, and flexibility. Define + whatever controller method signature you need, possibly using one of + the parameter annotations (such as @RequestParam, @RequestHeader, + @PathVariable, and more) for a given scenario. @@ -354,7 +323,7 @@ <servlet-mapping> <servlet-name>golfing</servlet-name> - <url-pattern>*.do</url-pattern> + <url-pattern>/golfing/*</url-pattern> </servlet-mapping> </web-app> @@ -554,11 +523,13 @@ + Parameter + Explanation @@ -608,533 +579,986 @@ input into a sensible model which will be represented to the user by the view. Spring has implemented the notion of a controller in a very abstract way enabling a wide variety of different kinds of controllers to be - created. Spring contains form-specific controllers, command-based - controllers, and controllers that execute wizard-style logic, to name but - a few. + created. - Spring's basis for the controller architecture is the - org.springframework.web.servlet.mvc.Controller - interface, the source code for which is listed below. + Spring 2.5 introduced an annotation-based programming model for MVC + controllers, using annotations such as + @RequestMapping, + @RequestParam, + @ModelAttribute, etc. This annotation + support is available for both Servlet MVC and Portlet MVC. Controllers + implemented in this style do not have to extend specific base classes or + implement specific interfaces. Furthermore, they do not usually have + direct dependencies on Servlet or Portlet API's, although they can easily + get access to Servlet or Portlet facilities if desired. - public interface Controller { + + The Spring distribution ships with the + PetClinic sample, which is a web application that + takes advantage of the annotation support described in this section, in + the context of simple form processing. You can find the + PetClinic application in the + 'samples/petclinic' directory. - /** - * Process the request and return a ModelAndView object which the DispatcherServlet - * will render. - */ - ModelAndView handleRequest( - HttpServletRequest request, - HttpServletResponse response) throws Exception; + For a further sample application that builds on annotation-based + Web MVC, check out imagedb. The focus in that + sample is on stateless multi-action controllers, including the + processing of multipart file uploads. You can find the + imagedb application in the + 'samples/imagedb' directory. + -} + @Controller +public class HelloWorldController { - As you can see, the Controller - interface defines a single method that is responsible for handling a - request and returning an appropriate model and view. These three concepts - are the basis for the Spring MVC implementation - - ModelAndView and - Controller. While the - Controller interface is quite abstract, - Spring offers a lot of Controller - implementations out of the box that already contain a lot of the - functionality you might need. The - Controller interface just defines the most - basic responsibility required of every controller; namely handling a - request and returning a model and a view. - -
- <classname>AbstractController</classname> and - <classname>WebContentGenerator</classname> - - To provide a basic infrastructure, all of Spring's various - Controller inherit from - AbstractController, a class offering caching - support and, for example, the setting of the mimetype. - - - Features offered by the - <classname>AbstractController</classname> - - - - - - - - Feature - Explanation - - - - - - supportedMethods - - indicates what methods this controller should accept. - Usually this is set to both GET and - POST, but you can modify this to reflect the - method you want to support. If a request is received with a - method that is not supported by the controller, the client will - be informed of this (expedited by the throwing of a - ServletException). - - - - requireSession - - indicates whether or not this controller requires a HTTP - session to do its work. If a session is not present when such a - controller receives a request, the user is informed of this by a - ServletException being thrown. - - - - synchronizeOnSession - - use this if you want handling by this controller to be - synchronized on the user's HTTP session. - - - - cacheSeconds - - when you want a controller to generate a caching - directive in the HTTP response, specify a positive integer here. - By default the value of this property is set to - -1 so no caching directives will be - included in the generated response. - - - - useExpiresHeader - - tweaks your controllers to specify the HTTP 1.0 - compatible "Expires" header in the - generated response. By default the value of this property is - true. - - - - useCacheHeader - - tweaks your controllers to specify the HTTP 1.1 - compatible "Cache-Control" header in the - generated response. By default the value of this property is - true. - - - -
- - When using the AbstractController as the - baseclass for your controllers you only have to override the - handleRequestInternal(HttpServletRequest, - HttpServletResponse) method, implement your logic, and return - a ModelAndView object. Here is short example - consisting of a class and a declaration in the web application - context. - - package samples; - -public class SampleController extends AbstractController { - - public ModelAndView handleRequestInternal( - HttpServletRequest request, - HttpServletResponse response) throws Exception { - - ModelAndView mav = new ModelAndView("hello"); + @RequestMapping("/helloWorld") + public ModelAndView helloWorld() { + ModelAndView mac = new ModelAndView(); + mav.setViewName("helloWorld"); mav.addObject("message", "Hello World!"); - return mav; + return mav; } } - <bean id="sampleController" class="samples.SampleController"> - <property name="cacheSeconds" value="120"/> -</bean> + As you can see, the @Controller and + @RequestMapping annotations allow for + flexible method names and signatures. In this particular example the + method has no parameters and returns a + ModelAndView, but various other (and better) + strategies exist, as will be explained later in this section. + ModelAndView, + @Controller, and + @RequestMapping form the basis for the + Spring MVC implementation. This section document these annotations and how + they are most commonly used in a Servlet environment. - The above class and the declaration in the web application context - is all you need besides setting up a handler mapping (see the section - entitled ) to get this very simple - controller working. This controller will generate caching directives - telling the client to cache things for 2 minutes before rechecking. This - controller also returns a hard-coded view (which is typically considered - bad practice). +
+ Defining a controller with + <interfacename>@Controller</interfacename> + + The @Controller annotation + indicates that a particular class serves the role of a + controller. There is no need to extend any + controller base class or reference the Servlet API. You are of course + still able to reference Servlet-specific features if you need to. + + The basic purpose of the + @Controller annotation is to act as a + stereotype for the annotated class, indicating its role. The dispatcher + will scan such annotated classes for mapped methods, detecting + @RequestMapping annotations (see the next + section). + + Annotated controller beans may be defined explicitly, using a + standard Spring bean definition in the dispatcher's context. However, + the @Controller stereotype also allows + for autodetection, aligned with Spring general support for detecting + component classes in the classpath and auto-registering bean definitions + for them. + + To enable autodetection of such annotated controllers, you have to + add component scanning to your configuration. This is easily achieved by + using the spring-context schema as shown in the + following XML snippet: + + <?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xmlns:p="http://www.springframework.org/schema/p" + xmlns:context="http://www.springframework.org/schema/context" + xsi:schemaLocation=" + http://www.springframework.org/schema/beans + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd + http://www.springframework.org/schema/context + http://www.springframework.org/schema/context/spring-context-3.0.xsd"> + + <context:component-scan base-package="org.springframework.samples.petclinic.web"/> + + // ... + +</beans>
-
- Other simple controllers +
+ Mapping requests with + <interfacename>@RequestMapping</interfacename> - Although you can extend AbstractController, - Spring provides a number of concrete implementations which offer - functionality that is commonly used in simple MVC applications. The - ParameterizableViewController is basically the - same as the example above, except for the fact that you can specify the - view name that it will return in the web application context (and thus - remove the need to hard-code the viewname in the Java class). + The @RequestMapping annotation is + used to map URLs like /appointments onto an entire + class or a particular handler method. It can be used to annotate both a + class or method, Typically the class-level annotation maps a specific + request path (or path pattern) onto a form controller, with additional + method-level annotations 'narrowing' the primary mapping for a specific + HTTP method request method ("GET"/"POST") or specific HTTP request + parameters. - The UrlFilenameViewController inspects the - URL and retrieves the filename of the file request and uses that as a - viewname. For example, the filename of - http://www.springframework.org/index.html request is - index. -
+ The following example shows a controller from the PetClinic sample + application that uses this annotation: -
- The <classname>MultiActionController</classname> + @Controller +@RequestMapping("/appointments") +public class AppointmentsController { - Spring offers a MultiActionController class - that supports the aggregation of multiple request-handling methods into - one controller, which then allows you to group related functionality - together. (If you are a Struts veteran you might recognize the - similarity between the Struts DispatchAction and - the Spring MVC MultiActionController.) The - MultiActionController class is defined in a - distinct package - - org.springframework.web.servlet.mvc.multiaction - and - it is capable of mapping requests to method names and then invoking the - correct method to handle a particular request. Using the - MultiActionController is especially handy when - you have a lot of related functionality that would perhaps be nice to - define all in a single class without having to implement one - Controller for each bit of functionality. - The MultiActionController typically is not - appropriate for capturing very complex request-handling logic or use - cases that address totally-different areas of functionality, and you are - encouraged to stick with the standard 'one - piece-of-functionality maps to one - Controller' for such - cases. + private AppointmentBook appointmentBook; + + @Autowired + public AppointmentsController(AppointmentBook appointmentBook) { + this.appointmentBook = appointmentBook; + } - There are two usage-styles for the - MultiActionController. Either you subclass the - MultiActionController and specify the methods - that will be resolved by the - MethodNameResolver on your subclass, or - you define a delegate object, on which methods resolved by the - MethodNameResolver will be invoked. If - you choose the former style, you do not need to set a delegate, but for - the latter style, you will need to inject your delegate object into the - MultiActionController as a collaborator (either - as a single constructor argument or via the - 'setDelegate' method). + @RequestMapping(method = RequestMethod.GET) + public Appointments get() { + return appointmentBook.getAppointmentsForToday(); + } - The MultiActionController needs some - strategy to determine which method to invoke when handling an incoming - request: this strategy is defined by the - MethodNameResolver interface. The - MultiActionController class exposes the - 'methodNameResolver' property so that you can inject - a MethodNameResolver that is capable of - doing that. The methods that you define on a - MultiActionController (or on the class of the - injected delegate object) must conform to the following - signature: + @RequestMapping(value="/{day}", method = RequestMethod.GET) + public void getForDay(@PathVariable Date day, ExternalContext context) { + Appointments appts = appointmentBook.getAppointmentsForDay(day); + context.getModel().addAttribute(appts); + context.selectView("appointments"); + if (context.isAjaxRequest()) { + //could activate a ViewHelper for component associated with main + context.renderFragment("main"); + } + } - // 'anyMeaningfulName' can be replaced by any method name -public [ModelAndView | Map | void] anyMeaningfulName(HttpServletRequest, HttpServletResponse [,HttpSession] [,AnyObject]) + @RequestMapping(value="/new", method = RequestMethod.GET) + public AppointmentForm getNewForm() { + return new AppointmentForm(); + } - The full details of this method signature are covered in the - class-level - Javadoc of the MultiActionController - source itself. If you are planning to use the - MultiActionController, you are highly encouraged - to consult that Javadoc. However, below you will find some basic - examples of valid MultiActionController method - signatures. - - The standard signature (mirrors the - Controller interface method). - - public ModelAndView displayCatalog(HttpServletRequest, HttpServletResponse) - - This signature accepts a Login argument - that will be populated (bound) with parameters retrieved from the - request. - - public ModelAndView login(HttpServletRequest, HttpServletResponse, Login) - - This signature requires that the request already have a valid - session. - - public ModelAndView viewCart(HttpServletRequest, HttpServletResponse, HttpSession) - - This signature accepts a Product argument - that will be populated (bound) with parameters retrieved from the - request and requires that the request already have - a valid session. Note that the order of arguments is important: the - session must be the third argument, and an object to be bound must - always be the final argument (fourth when a session is specified, or - third otherwise). - - public ModelAndView updateCart(HttpServletRequest, HttpServletResponse, HttpSession, Product) - - This signature has a void return type - indicating that the handler method assumes the responsibility of writing - the response. - - public void home(HttpServletRequest, HttpServletResponse) - - This signature has a Map return - type indicating that a view name translator will be responsible for - providing the view name based upon the request, and the model will - consist of the Map's entries (see the - section entitled below). - - public Map list(HttpServletRequest, HttpServletResponse) - - The MethodNameResolver is - responsible for resolving method names based on the specifics of the - incoming HttpServletRequest. A number of - MethodNameResolver implementations are - provided for you, and of course you can always write your own. Please - also note that the InternalPathMethodNameResolver - is the default MethodNameResolver that - will be used if you don't inject one explicitly. - - - - InternalPathMethodNameResolver - - interprets the final filename from the request path and uses that as - the method name/ - - For example, - 'http://www.sf.net/testing.view' will result in - the method testing(HttpServletRequest, - HttpServletResponse) being invoked. - - - - ParameterMethodNameResolver - - interprets a request parameter as the name of the method that is to - be invoked. - - For example, - 'http://www.sf.net/index.view?method=testIt' will - result in the method testIt(HttpServletRequest, - HttpServletResponse) being invoked. The - 'paramName' property specifies the name of the - request parameter that is to be used. - - - - PropertiesMethodNameResolver - uses a - user-defined Properties object with request - URLs mapped to method names. For example, when the - Properties contain - '/index/welcome.html=doIt' and a request to - /index/welcome.html comes in, the - doIt(HttpServletRequest, - HttpServletResponse) method will be invoked. This - particular MethodNameResolver uses - the Spring PathMatcher class internally, so - if the Properties contained - '/**/welcom?.html', the example would also have - worked. - - - - You may also declare custom methods for handling - Exceptions that occur during request handling. - The valid signature for such a method is similar to the request handling - methods in that the HttpServletRequest - and HttpServletResponse must be provided - as the first and second parameters respectively. Unlike request handling - methods however, the method's name is irrelevant. Instead, when - determining which Exception handling method to - invoke, the decision is based upon the most specific possible match - among the methods whose third argument is some type of - Exception. Here is an example signature for one - such Exception handling method. - - public ModelAndView processException(HttpServletRequest, HttpServletResponse, IllegalArgumentException) - - Let's look at an example showing the delegate-style of - MultiActionController usage in conjunction with - the ParameterMethodNameResolver. - - <bean id="paramMultiController" - class="org.springframework.web.servlet.mvc.multiaction.MultiActionController"> - - <property name="methodNameResolver"> - <bean class="org.springframework.web.servlet.mvc.multiaction.ParameterMethodNameResolver"> - <property name="paramName" value="method"/> - </bean> - </property> - - <property name="delegate"> - <bean class="samples.SampleDelegate"/> - </property> - -</bean> -} - - public class SampleDelegate { - - public ModelAndView retrieveIndex(HttpServletRequest req, HttpServletResponse resp) { - return new ModelAndView("index", "date", new Long(System.currentTimeMillis())); + @RequestMapping(method = RequestMethod.POST) + public String post(AppointmentForm form) { + appointmentBook.createAppointment(form); + return "redirect:/appointments"; } } - When using the delegate shown above, we could also configure the - PropertiesMethodNameResolver to match any number - couple of URLs to the method we defined: + In the example, above, we see that the + @RequestMapping is used in a number of + places. The first usage is on the type (class) level, which indicates + that all handling methods on this controller will be relative to the + /appointments path. Next, we see that the + get() method has a further + @RequestMapping refinement: it only + accepts GET requests, meaning that an HTTP GET for + /appointments will result in this method being + invoked. The post() has a similar refinement, + and the getNewForm() combines the definition of + HTTP method and path into one, so that GET requests for + appointments/new are handled by that method. - <bean id="propsResolver" - class="org....mvc.multiaction.PropertiesMethodNameResolver"> - <property name="mappings"> - <value> -/index/welcome.html=retrieveIndex -/**/notwelcome.html=retrieveIndex -/*/user?.html=retrieveIndex - </value> - </property> + The getForDay() method shows another + usage of @RequestMapping: URI templates. + We will discuss these in the next section + . + + A @RequestMapping on the class + level is not required. Without it, all paths are simply absolute, and + not relative. The following is an example of a multi-action controller + from the PetClinic sample application using + @RequestMapping: + + @Controller +public class ClinicController { + + private final Clinic clinic; + + @Autowired + public ClinicController(Clinic clinic) { + this.clinic = clinic; + } + + @RequestMapping("/") + public void welcomeHandler() { + } + + @RequestMapping("/vets") + public ModelMap vetsHandler() { + return new ModelMap(this.clinic.getVets()); + } + +} + +
+ URI Templates + + To easily access (parts of) a request URL in your handling + methods, Spring MVC allows for the use of URI + templates in the + @RequestMapping path value. + + + URI Templates + + A URI Template is a URI-like string, containing one or more + variable names. When these variables are substituted for values, the + template becomes a URI The proposed + RFC for URI Templates defines how an URI is parameterized. + For example, the URI Template + + http://www.example.com/users/{userid} + + contains the variable 'userid'. If we assign the variable the + value "fred", then 'expanding' the URI Template gives. + + http://www.example.com/users/fred + + When processing a request the URI can be compared to an + expected URI Template in order to extract a collection of + variables. + + + The @PathVariable method + parameter annotation is used to indicate that a method parameter + should be bound to the value of a URI template variable. + + The following code snippet shows the use of a single + @PathVariable in a controller + method: + + @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) +public String findOwner(@PathVariable String ownerId, Model model) { + Owner owner = ownerService.findOwner(ownerId); + model.addAttribute("owner", owner); + return "displayOwner"; +} + + + The URI Template "/owners/{ownerId}" + specifies the variable name ownerId. When the controller handles this + request, the value of ownerId is set the value in the request URI. For + example, when a request comes in for /owners/fred, the value 'fred' is + bound to the method parameter String + ownerId. + + The matching of method parameter names to URI Template variable + names can only be done if your code is compiled with debugging + enabled. If you do have not debugging enabled, you must specify the + name of the URI Template variable name to bind to in the @PathVariable + annotation. For example: + + @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) +public String findOwner(@PathVariable("ownerId") String ownerId, Model model) { + // implementation omitted +} + + + The name of the method parameter does not matter in this case, + so you may also use a controller method with the signature shown + below + + @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) +public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { + // implementation omitted +} + + Multiple @PathVariable annotations can be used to bind to + multiple URI Template variables as shown below: + + @RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) +public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + Owner owner = ownerService.findOwner(ownderId); + Pet pet = owner.getPet(petId); + model.addAttribute("pet", pet); + return "displayPet"; +} + + + The following code snippet shows the use of path variables on a + relative path, so that the findPet() method + will be invoked for /owners/42/pets/21, for + instance. + + @Controller +@RequestMapping("/owners/{ownerId}") +public class RelativePathUriTemplateController { + + @RequestMapping("/pets/{petId}") + public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + // implementation omitted + } +} + + + + Method parameters that are decorated with the + @PathVariable annotation can be of + any simple type such as int, long, + Date... Spring automatically converts to the appropriate type and + throws a TypeMismatchException if the type is + not correct. You can further customizing this conversion process by + customizing the data binder, see . + +
+ +
+ Advanced <interfacename>@RequestMapping</interfacename> + options + + In addition to URI templates, the + @RequestMapping annotation also + supports Ant-style path patterns (e.g. + /myPath/*.do). A combination of URI templates and + Ant-style globs is also supported (e.g. + /owners/*/pets/{petId}). + + The handler method names are taken into account for narrowing if + no path was specified explicitly, according to the specified + org.springframework.web.servlet.mvc.multiaction.MethodNameResolver + (by default an + org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver). + Note that this only applies in case of ambiguous annotation mappings + that do not specify a path mapping explicitly. In other words, the + method name is only used for narrowing among a set of matching + methods; it does not constitute a primary path mapping itself. + + If you have a single default method (without explicit path + mapping), then all requests without a more specific mapped method + found will be dispatched to it. If you have multiple such default + methods, then the method name will be taken into account for choosing + between them. + + Path mappings can be narrowed through parameter conditions: a + sequence of "myParam=myValue" style expressions, with a request only + mapped if each such parameter is found to have the given value. For + example: @Controller +@RequestMapping("/owners/{ownerId}") +public class RelativePathUriTemplateController { + + @RequestMapping(value = "/pets/{petId}", params="myParam=myValue") + public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { + // implementation omitted + } +} + "myParam" style expressions are also supported, with such + parameters having to be present in the request (allowed to have any + value). Finally, "!myParam" style expressions indicate that the + specified parameter is not supposed to be present + in the request. + + Similarly, path mappings can be narrowed down through header + conditions: @Controller +@RequestMapping("/owners/{ownerId}") +public class RelativePathUriTemplateController { + +@RequestMapping(value = "/pets", method = RequestMethod.POST, headers="content-type=text/*") + public void addPet(Pet pet, @PathVariable String ownerId) { + // implementation omitted + } +} + In the above example, the addPet + will only be invoked when the Content-Type is in the + text/* range, for instance + text/xml. +
+ +
+ Supported handler method arguments and return types + + Handler methods which are annotated with + @RequestMapping are allowed to have very + flexible signatures. They may have arguments of the following types, + in arbitrary order (except for validation results, which need to + follow right after the corresponding command object, if desired): + + + Request and/or response objects (Servlet API). You may + choose any specific request/response type, e.g. + ServletRequest / + HttpServletRequest. + + + + Session object (Servlet API): of type + HttpSession. An argument of this + type will enforce the presence of a corresponding session. As a + consequence, such an argument will never be + null. + + + Note that session access may not be thread-safe, in + particular in a Servlet environment: Consider switching the + AnnotationMethodHandlerAdapter's + "synchronizeOnSession" flag to "true" if multiple requests are + allowed to access a session concurrently. + + + + + org.springframework.web.context.request.WebRequest + or + org.springframework.web.context.request.NativeWebRequest. + Allows for generic request parameter access as well as + request/session attribute access, without ties to the native + Servlet/Portlet API. + + + + java.util.Locale for the current + request locale (determined by the most specific locale resolver + available, i.e. the configured + LocaleResolver in a Servlet + environment). + + + + java.io.InputStream / + java.io.Reader for access to the + request's content. This will be the raw InputStream/Reader as + exposed by the Servlet API. + + + + java.io.OutputStream / + java.io.Writer for generating the + response's content. This will be the raw OutputStream/Writer as + exposed by the Servlet API. + + + + @PathVariabe annotated parameters + for access to URI template variables, see . + + + + @RequestParam annotated parameters + for access to specific Servlet request parameters. Parameter + values will be converted to the declared method argument type. + See . + + + + @RequestHeader annotated parameters + for access to specific Servlet request HTTP headers. Parameter + values will be converted to the declared method argument + type. + + + + @RequestBody annotated parameters + for access to the request HTTP body. Parameter values will be + converted to the declared method argument type using + HttpMessageConverters. See . + + + + java.util.Map / + org.springframework.ui.Model / + org.springframework.ui.ModelMap for + enriching the implicit model that will be exposed to the web + view. + + + + Command/form objects to bind parameters to: as bean + properties or fields, with customizable type conversion, + depending on @InitBinder methods and/or + the HandlerAdapter configuration - see the + "webBindingInitializer" property on + AnnotationMethodHandlerAdapter. Such + command objects along with their validation results will be + exposed as model attributes, by default using the non-qualified + command class name in property notation (e.g. "orderAddress" for + type "mypackage.OrderAddress"). Specify a parameter-level + ModelAttribute annotation for declaring a + specific model attribute name. + + + + org.springframework.validation.Errors + / + org.springframework.validation.BindingResult + validation results for a preceding command/form object (the + immediate preceding argument). + + + + org.springframework.web.bind.support.SessionStatus + status handle for marking form processing as complete + (triggering the cleanup of session attributes that have been + indicated by the @SessionAttributes + annotation at the handler type level). + + + + The following return types are supported for handler methods: + + + A ModelAndView object, with the + model implicitly enriched with command objects and the results + of @ModelAttribute annotated reference data + accessor methods. + + + + A Model object, with the + view name implicitly determined through a + RequestToViewNameTranslator and + the model implicitly enriched with command objects and the + results of @ModelAttribute annotated + reference data accessor methods. + + + + A Map object for exposing a + model, with the view name implicitly determined through a + RequestToViewNameTranslator and + the model implicitly enriched with command objects and the + results of @ModelAttribute annotated + reference data accessor methods. + + + + A View object, with the + model implicitly determined through command objects and + @ModelAttribute annotated reference data + accessor methods. The handler method may also programmatically + enrich the model by declaring a + Model argument (see + above). + + + + A String value which is interpreted + as view name, with the model implicitly determined through + command objects and @ModelAttribute annotated + reference data accessor methods. The handler method may also + programmatically enrich the model by declaring a + Model argument (see + above). + + + + void if the method handles the response + itself (by writing the response content directly, declaring an + argument of type ServletResponse + / HttpServletResponse for that + purpose) or if the view name is supposed to be implicitly + determined through a + RequestToViewNameTranslator (not + declaring a response argument in the handler method + signature). + + + + If the method is annotated with + @ResponseBody, the return type + will be written to the response HTTP body. The return value will + be converted to the declared method argument type using + HttpMessageConverters. See . + + + + Any other return type will be considered as single model + attribute to be exposed to the view, using the attribute name + specified through @ModelAttribute at the + method level (or the default attribute name based on the return + type's class name otherwise). The model will be implicitly + enriched with command objects and the results of + @ModelAttribute annotated reference data + accessor methods. + + +
+ +
+ Binding request parameters to method parameters with + <classname>@RequestParam</classname> + + The @RequestParam annotation is used to + bind request parameters to a method parameter in your + controller. + + The following code snippet shows the usage: + + @Controller +@RequestMapping("/pets") +@SessionAttributes("pet") +public class EditPetForm { + + // ... + + @RequestMapping(method = RequestMethod.GET) + public String setupForm(@RequestParam("petId") int petId, ModelMap model) { + Pet pet = this.clinic.loadPet(petId); + model.addAttribute("pet", pet); + return "petForm"; + } + + // ... + + + Parameters using this annotation are required by default, but + you can specify that a parameter is optional by setting + @RequestParam's + required attribute to false + (e.g., @RequestParam(value="id", + required="false")). +
+ +
+ Mapping the request body with the @RequestBody + annotation + + The @RequestBody method parameter + annotation is used to indicate that a method parameter should be bound + to the value of the HTTP request body. For example, + + @RequestMapping(value = "/something", method = RequestMethod.PUT) +public void handle(@RequestBody String body, Writer writer) throws IOException { + writer.write(body); +} + + The conversion of the request body to the method argument is + done using a HttpMessageConverter. + HttpMessageConverter is responsible for + converting from the HTTP request message to an object and converting + from an object to the HTTP response body. + DispatcherServlet supports annotation based + processing using the + DefaultAnnotationHandlerMapping and + AnnotationMethodHandlerAdapter. In Spring 3 the + AnnotationMethodHandlerAdapter has been + extended to support the @RequestBody and has + several HttpMessageConverters + registered by default, these are + + + + ByteArrayHttpMessageConverter - + converts byte arrays + + + + StringHttpMessageConverter - converts + strings + + + + FormHttpMessageConverter - converts + form data to/from a MultiValueMap<String, String> + + + + SourceHttpMessageConverter - converts + to/from a javax.xml.transform.Source; + + + + MarshallingHttpMessageConverter - + converts to/from an object using the + org.springframework.oxm package. + + + + More information on these converters can be found in the section + Message + Converters. + + The MarshallingHttpMessageConverter + requires a Marshaller and + Unmarshaller from the + org.springframework.oxm package to be + configured on an instance of + AnnotationMethodHandlerAdapter in the + application context. For example + + <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> + <property name="messageConverters"> + <util:list id="beanList"> + <ref bean="stringHttpMessageConverter"/> + <ref bean="marshallingHttpMessageConverter"/> + </util:list> + </property </bean> -<bean id="paramMultiController" class="org....mvc.multiaction.MultiActionController"> +<bean id="stringHttpMessageConverter" + class="org.springframework.http.converter.StringHttpMessageConverter"/> - <property name="methodNameResolver" ref="propsResolver"/> - <property name="delegate"> - <bean class="samples.SampleDelegate"/> +<bean id="marshallingHttpMessageConverter" + class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> + <property name="marshaller" ref="castorMarshaller" /> + <property name="unmarshaller" ref="castorMarshaller" /> +</bean> + +<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> + +
+ +
+ Mapping the response body with the @ResponseBody + annotation + + Similar to @RequestBody, there is + the @ResponseBody annotation. This + annotation can be put on a method and indicates that the return type + should be written straight to the HTTP response body (and not placed + in a Model, or interpreted as a view name). For example, + + @RequestMapping(value = "/something", method = RequestMethod.PUT) +@ResponseBody +public String helloWorld() { + return "Hello World"; +} + + The example above will result in the text Hello + World being written to the HTTP response stream. + + Just like @RequestBody, the + conversion of the returned object to response body is done using a + HttpMessageConverter. More information + on these converters can be found in the previous section, or in the + section Message + Converters. +
+ +
+ Providing a link to data from the model with + <classname>@ModelAttribute</classname> + + @ModelAttribute has two usage scenarios + in controllers. When placed on a method parameter, + @ModelAttribute is used to map a model + attribute to the specific, annotated method parameter (see the + processSubmit() method below). This is how the + controller gets a reference to the object holding the data entered in + the form. + + @ModelAttribute is also used at the + method level to provide reference data for the + model (see the populatePetTypes() method below). + For this usage the method signature can contain the same types as + documented above for the @RequestMapping + annotation. + + + Note: + @ModelAttribute annotated methods will be + executed before the chosen + @RequestMapping annotated handler method. + They effectively pre-populate the implicit model with specific + attributes, often loaded from a database. Such an attribute can then + already be accessed through @ModelAttribute + annotated handler method parameters in the chosen handler method, + potentially with binding and validation applied to it. + + + The following code snippet shows these two usages of this + annotation: + + @Controller +@RequestMapping("/owners/{ownerId}/pets/{petId}/edit") +@SessionAttributes("pet") +public class EditPetForm { + + // ... + + @ModelAttribute("types") + public Collection<PetType> populatePetTypes() { + return this.clinic.getPetTypes(); + } + + @RequestMapping(method = RequestMethod.POST) + public String processSubmit( + @ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { + + new PetValidator().validate(pet, result); + if (result.hasErrors()) { + return "petForm"; + } + else { + this.clinic.storePet(pet); + status.setComplete(); + return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); + } + } + +} +
+ +
+ Specifying attributes to store in a Session with + <classname>@SessionAttributes</classname> + + The type-level @SessionAttributes + annotation declares session attributes used by a specific handler. + This will typically list the names of model attributes which should be + transparently stored in the session or some conversational storage, + serving as form-backing beans between subsequent requests. + + The following code snippet shows the usage of this + annotation: + + @Controller +@RequestMapping("/editPet.do") +@SessionAttributes("pet") +public class EditPetForm { + // ... +} + +
+ +
+ Mapping cookie values with the @CookieValue annotation + + The @CookieValue annotation + allows a method parameter to be bound to the value of an HTTP + cookie. + + Let us consider that the following cookie has been received with + an http request: + + JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 + + The following code sample allows you to easily get the value of + the "JSESSIONID"cookie: + + @RequestMapping("/displayHeaderInfo.do") +public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) { + + //... + +} + + This annotation is supported for annotated handler methods in + Servlet and Portlet environments. +
+ +
+ Mapping request header attributes with the @RequestHeader + annotation + + The @RequestHeader annotation + allows a method parameter to be bound to a request header. + + Here is a request header sample: + + +Host localhost:8080 +Accept text/html,application/xhtml+xml,application/xml;q=0.9 +Accept-Language fr,en-gb;q=0.7,en;q=0.3 +Accept-Encoding gzip,deflate +Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7 +Keep-Alive 300 + + + The following code sample allows you to easily get the value of + the "Accept-Encoding" and "Keep-Alive" headers: + + @RequestMapping("/displayHeaderInfo.do") + public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, + @RequestHeader("Keep-Alive") long keepAlive) { + + //... + +} + + This annotation is supported for annotated handler methods in + Servlet and Portlet environments. +
+ +
+ Customizing <classname>WebDataBinder</classname> + initialization + + To customize request parameter binding with PropertyEditors, + etc. via Spring's WebDataBinder, you can either + use @InitBinder-annotated methods + within your controller or externalize your configuration by providing + a custom WebBindingInitializer. + +
+ Customizing data binding with + <interfacename>@InitBinder</interfacename> + + Annotating controller methods with + @InitBinder allows you to configure + web data binding directly within your controller class. + @InitBinder identifies methods which + initialize the WebDataBinder which will be + used for populating command and form object arguments of annotated + handler methods. + + Such init-binder methods support all arguments that + @RequestMapping supports, except for + command/form objects and corresponding validation result objects. + Init-binder methods must not have a return value. Thus, they are + usually declared as void. Typical arguments + include WebDataBinder in combination with + WebRequest or + java.util.Locale, allowing code to register + context-specific editors. + + The following example demonstrates the use of + @InitBinder for configuring a + CustomDateEditor for all + java.util.Date form properties. + + @Controller +public class MyFormController { + + @InitBinder + public void initBinder(WebDataBinder binder) { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setLenient(false); + binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); + } + + // ... +} +
+ +
+ Configuring a custom + <interfacename>WebBindingInitializer</interfacename> + + To externalize data binding initialization, you can provide a + custom implementation of the + WebBindingInitializer interface, + which you then enable by supplying a custom bean configuration for + an AnnotationMethodHandlerAdapter, thus + overriding the default configuration. + + The following example from the PetClinic application shows a + configuration using a custom implementation of the + WebBindingInitializer interface, + org.springframework.samples.petclinic.web.ClinicBindingInitializer, + which configures PropertyEditors required by several of the + PetClinic controllers. + + <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> + <property name="cacheSeconds" value="0" /> + <property name="webBindingInitializer"> + <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> </property> - -</bean> -
- -
- Command controllers - - Spring's command controllers are a - fundamental part of the Spring Web MVC package. Command controllers - provide a way to interact with data objects and dynamically bind - parameters from the HttpServletRequest to - the data object specified. They perform a somewhat similar role to the - Struts ActionForm, but in Spring, your data - objects don't have to implement a framework-specific interface. First, - lets examine what command controllers are available straight out of the - box. - - - - AbstractCommandController - a command - controller you can use to create your own command controller, - capable of binding request parameters to a data object you specify. - This class does not offer form functionality; it does however offer - validation features and lets you specify in the controller itself - what to do with the command object that has been populated with - request parameter values. - - - - AbstractFormController - an abstract - controller offering form submission support. Using this controller - you can model forms and populate them using a command object you - retrieve in the controller. After a user has filled the form, the - AbstractFormController binds the fields, - validates the command object, and hands the object back to the - controller to take the appropriate action. Supported features are: - invalid form submission (resubmission), validation, and normal form - workflow. You implement methods to determine which views are used - for form presentation and success. Use this controller if you need - forms, but don't want to specify what views you're going to show the - user in the application context. - - - - SimpleFormController - a form - controller that provides even more support when creating a form with - a corresponding command object. The - SimpleFormController let's you specify a - command object, a viewname for the form, a viewname for page you - want to show the user when form submission has succeeded, and - more. - - - - AbstractWizardFormController - as the - class name suggests, this is an abstract class - your wizard - controller should extend it. This means you have to implement the - validatePage(), - processFinish() and - processCancel() methods. - - You probably also want to write a contractor, which should at - the very least call setPages() and - setCommandName(). The former takes as its - argument an array of type String. This array is the list of views - which comprise your wizard. The latter takes as its argument a - String, which will be used to refer to your command object from - within your views. - - As with any instance of - AbstractFormController, you are required to - use a command object - a JavaBean which will be populated with the - data from your forms. You can do this in one of two ways: either - call setCommandClass() from the constructor with - the class of your command object, or implement the - formBackingObject() method. - - AbstractWizardFormController has a - number of concrete methods that you may wish to override. Of these, - the ones you are likely to find most useful are: - referenceData(..) which you can use to pass model - data to your view in the form of a - Map; - getTargetPage() if your wizard needs to change - page order or omit pages dynamically; and - onBindAndValidate() if you want to override the - built-in binding and validation workflow. - - Finally, it is worth pointing out the - setAllowDirtyBack() and - setAllowDirtyForward(), which you can call from - getTargetPage() to allow users to move backwards - and forwards in the wizard even if validation fails for the current - page. - - For a full list of methods, see the Javadoc for - AbstractWizardFormController. There is an - implemented example of this wizard in the jPetStore included in the - Spring distribution: - org.springframework.samples.jpetstore.web.spring.OrderFormController. - - +</bean> + +
+
Handler mappings - Using a handler mapping you can map incoming web requests to - appropriate handlers. There are some handler mappings you can use out of - the box, for example, the SimpleUrlHandlerMapping - or the BeanNameUrlHandlerMapping, but let's first - examine the general concept of a - HandlerMapping. - - The functionality a basic - HandlerMapping provides is the delivering - of a HandlerExecutionChain, which must contain the - handler that matches the incoming request, and may also contain a list of - handler interceptors that are applied to the request. When a request comes - in, the DispatcherServlet will hand it over to the - handler mapping to let it inspect the request and come up with an - appropriate HandlerExecutionChain. Then the - DispatcherServlet will execute the handler and - interceptors in the chain (if any). - - The concept of configurable handler mappings that can optionally - contain interceptors (executed before or after the actual handler was - executed, or both) is extremely powerful. A lot of supporting - functionality can be built into custom - HandlerMappings. Think of a custom handler - mapping that chooses a handler not only based on the URL of the request - coming in, but also on a specific state of the session associated with the - request. - - This section describes two of Spring's most commonly used handler - mappings. They both extend the AbstractHandlerMapping - and share the following properties: + In previous versions of Spring MVC, users were required to define HandlerMappings in + the web application context + to map incoming web requests to + appropriate handlers. With the introduction of Spring 2.5, the DispatcherServlet enables the + DefaultAnnotationHandlerMapping, which looks for + @RequestMapping annotations on @Controllers. + Typically, you do not need to override this default mapping, except when overriding the properties. + These properties are: + @@ -1191,139 +1615,16 @@ public [ModelAndView | Map | void] anyMeaningfulName(HttpServletRequest, HttpSer subclasses of org.springframework.web.servlet.handler.AbstractUrlHandlerMapping). -
- <classname>BeanNameUrlHandlerMapping</classname> + The following example shows how to override the default mapping, and add an interceptor: - A very simple, but very powerful handler mapping is the - BeanNameUrlHandlerMapping, which maps incoming - HTTP requests to names of beans, defined in the web application context. - Let's say we want to enable a user to insert an account and we've - already provided an appropriate form controller (see for more information on command- and - form controllers) and a JSP view (or Velocity template) that renders the - form. When using the BeanNameUrlHandlerMapping, - we could map the HTTP request with the URL - http://samples.com/editaccount.form to the - appropriate form Controller as - follows: + + + + + + - <beans> - <bean id="handlerMapping" class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/> - - <bean name="/editaccount.form" class="org.springframework.web.servlet.mvc.SimpleFormController"> - <property name="formView" value="account"/> - <property name="successView" value="account-created"/> - <property name="commandName" value="account"/> - <property name="commandClass" value="samples.Account"/> - </bean> -<beans> - - All incoming requests for the URL - /editaccount.form will now be handled by the form - Controller in the source listing above. - Of course we have to define a servlet-mapping in - web.xml as well, to let through all the requests - ending with .form. - - <web-app> - ... - <servlet> - <servlet-name>sample</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>1</load-on-startup> - </servlet> - - <!-- maps the sample dispatcher to *.form --> - <servlet-mapping> - <servlet-name>sample</servlet-name> - <url-pattern>*.form</url-pattern> - </servlet-mapping> - ... -</web-app> - - - If you want to use the - BeanNameUrlHandlerMapping, you don't - necessarily have to define it in the web application context (as - indicated above). By default, if no handler mapping can be found in - the context, the DispatcherServlet creates a - BeanNameUrlHandlerMapping for you! - -
- -
- <classname>SimpleUrlHandlerMapping</classname> - - A further - and much more powerful handler mapping - is the - SimpleUrlHandlerMapping. This mapping is - configurable in the application context and has Ant-style path matching - capabilities (see the Javadoc for the - org.springframework.util.PathMatcher class). Here - is an example: - - <web-app> - ... - <servlet> - <servlet-name>sample</servlet-name> - <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> - <load-on-startup>1</load-on-startup> - </servlet> - - <!-- maps the sample dispatcher to *.form --> - <servlet-mapping> - <servlet-name>sample</servlet-name> - <url-pattern>*.form</url-pattern> - </servlet-mapping> - - <!-- maps the sample dispatcher to *.html --> - <servlet-mapping> - <servlet-name>sample</servlet-name> - <url-pattern>*.html</url-pattern> - </servlet-mapping> - ... -</web-app> - - The above web.xml configuration snippet enables - all requests ending with .html and .form to be - handled by the sample dispatcher servlet. - - <beans> - - <!-- no 'id' required, HandlerMapping beans are automatically detected by the DispatcherServlet --> - <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping"> - <property name="mappings"> - <value> - /*/account.form=editAccountFormController - /*/editaccount.form=editAccountFormController - /ex/view*.html=helpController - /**/help.html=helpController - </value> - </property> - </bean> - - <bean id="helpController" - class="org.springframework.web.servlet.mvc.UrlFilenameViewController"/> - - <bean id="editAccountFormController" - class="org.springframework.web.servlet.mvc.SimpleFormController"> - <property name="formView" value="account"/> - <property name="successView" value="account-created"/> - <property name="commandName" value="Account"/> - <property name="commandClass" value="samples.Account"/> - </bean> -<beans> - - This handler mapping routes requests for - 'help.html' in any directory to the - 'helpController', which is a - UrlFilenameViewController (more about controllers - can be found in the section entitled ). - Requests for a resource beginning with 'view', and - ending with '.html' in the directory - 'ex' will be routed to the - 'helpController'. Two further mappings are also - defined for 'editAccountFormController'. -
+]]>
Intercepting requests - the @@ -1536,6 +1837,15 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { <classname>FreeMarkerView</classname> respectively and custom subclasses of them.</entry> </row> + + <row> + <entry><classname>ContentNegotiatingViewResolver</classname></entry> + + <entry>An implementation of the <interfacename>ViewResolver</interfacename> + interface that that resolves a view based on the request file name or <literal>Accept</literal> header. + See <xref linkend="mvc-multiple-representations"/>. + </entry> + </row> </tbody> </tgroup> </table> @@ -1757,6 +2067,156 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { happening in terms of handling the response.</para> </section> </section> + + <section id="mvc-multiple-representations"> + <title><classname>ContentNegotiatingViewResolver</classname> + + The ContentNegotiatingViewResolver + does not resolve views itself, but rather delegates to other + view resolvers, selecting the view that resembles the representation + requested by the client. + There are two strategies for a client to inform the server of + the representation it is interested in receiving. + + The first strategy is to use a distinct URI for each resource. + This is typically done by using a different file extension in the URI. + For example the URI + http://www.example.com/users/fred.pdf requests a PDF + representation of the user fred while + http://www.example.com/users/fred.xml requests an XML + representation. + + The second strategy is for the client to use the same URI to + locate the resource but set the Accept HTTP request + header to list the media + types that it understands. For example, a HTTP request for + http://www.example.com/users/fred with an + Accept header set to application/pdf + requests a PDF representation of the user fred while + http://www.example.com/users/fred with an + Accept header set to text/xml + requests an XML representation. This strategy is known as content + negotiation. + + + One issue with the Accept header is that is impossible to change + it in a web browser, in HTML. For instance, in Firefox, it's fixed + to + + Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 + + + For this reason it is common to see the use of a distinct URI + for each representation. + + + To support multiple representations of a resource Spring provides + the ContentNegotiatingViewResolver to resolve a + view based on the file extension or Accept header of + the HTTP request. ContentNegotiatingViewResolver + does not perform the view resolution itself, but instead delegates to a + list of view resolvers set using the bean property + ViewResolvers. + + The ContentNegotiatingViewResolver selects + an appropriate View to handle the request by + comparing the request media type(s) with the media type (a.k.a. + Content-Type) supported by the + View associated with each of its + ViewResolvers. The first + View in the list that has a compatible + Content-Type is used to return the representation to + the client. The Accept header may include wild cards, + for example 'text/*', in which case a View whose + Context-Type was 'text/xml' is a compatible match. + + To support the resolution of a view based on a file extension, + ContentNegotiatingViewResolver's bean property + MediaTypes is used to specify a mapping of file + extensions to media types. For more information on the algorithm to + determine the request media type, refer to the API documentation for + ContentNegotiatingViewResolver.. + + Here is an example configuration of a + ContentNegotiatingViewResolver + + + + + + + + + + + + + + + + + + + + +]]> + + The InternalResourceViewResolver handles + the translation of view names and JSP pages while the + BeanNameViewResolver returns a view based on the + name of a bean. (See "Resolving views - the ViewResolver + interface" for more details on how Spring looks up and + instantiates a view.) In this example, the content + bean is a class that inherits from + AbstractAtomFeedView which returns an Atom RSS + feed. For more information on creating an Atom Feed representation see + the section 'Atom Views'. + + In this configuration, if a request is made with a .html extension + the view resolver will look for a view that matches the text/html media + type. The InternalResourceViewResolver provides + the matching view for text/html. If the request is made with the file + extension .atom, the view resolver will look for a view that matches the + application/atom+xml media type. This view is provided by the + BeanNameViewResolver that maps to the + SampleContentAtomView if the view name returned + is 'content'. Alternatively, client requests could be made without a + file extension and setting the Accept header to the preferred media-type + and the same resolution of request to views would occur. + + + If ContentNegotiatingViewResolver's list + of ViewResolvers is not configured explicitly, then it will + automatically use any ViewResolvers defined in the application + context. + + + The corresponding controller code that returns an Atom RSS feed + for a URI of the form http://localhost/content.atom + or http://localhost/content with an + Accept header of application/atom+xml is shown + below + + @Controller + public class ContentController { + + private List<SampleContent> contentList = new ArrayList<SampleContent>(); + + @RequestMapping(value="/content", method=RequestMethod.GET) + public ModelAndView getContent() { + ModelAndView mav = new ModelAndView(); + mav.setViewName("content"); + mav.addObject("sampleContentList", contentList); + return mav; + } + + } + +
+
@@ -2059,9 +2519,9 @@ background=/themes/cool/img/coolBg.jpg package. Out of the box, Spring provides a MultipartResolver for use with Commons FileUpload (). - How uploading files is supported will be described in the rest of - this chapter. + url="http://jakarta.apache.org/commons/fileupload">). How + uploading files is supported will be described in the rest of this + chapter. By default, no multipart handling will be done by Spring, as some developers will want to handle multiparts themselves. You will have to @@ -2686,680 +3146,6 @@ public class FileUploadBean {
-
- Annotation-based controller configuration - - There is a current trend to favor annotations over XML files for - some types of configuration data. To facilitate this, Spring is now (since - 2.5) providing support for configuring the MVC framework components using - annotations. - - Spring 2.5 introduces an annotation-based programming model for MVC - controllers, using annotations such as - @RequestMapping, - @RequestParam, - @ModelAttribute, etc. This annotation - support is available for both Servlet MVC and Portlet MVC. Controllers - implemented in this style do not have to extend specific base classes or - implement specific interfaces. Furthermore, they do not usually have - direct dependencies on Servlet or Portlet API's, although they can easily - get access to Servlet or Portlet facilities if desired. - - - The Spring distribution ships with the - PetClinic sample, which is a web application that takes - advantage of the annotation support described in this section, in the context - of simple form processing. You can find the PetClinic - application in the 'samples/petclinic' directory. - - For a further sample application that builds on annotation-based Web MVC, - check out imagedb. The focus in that sample is on stateless - multi-action controllers, including the processing of multipart file uploads. - You can find the imagedb application in the - 'samples/imagedb' directory. - - - The following sections document these annotations and how they are - most commonly used in a Servlet environment. - -
- Setting up the dispatcher for annotation support - - @RequestMapping will only be processed - if a corresponding HandlerMapping (for type level annotations) - and/or HandlerAdapter (for method level annotations) is - present in the dispatcher. This is the case by default in both - DispatcherServlet and DispatcherPortlet. - - However, if you are defining custom HandlerMappings or - HandlerAdapters, then you need to make sure that a - corresponding custom DefaultAnnotationHandlerMapping - and/or AnnotationMethodHandlerAdapter is defined as well - - provided that you intend to use @RequestMapping. - - <?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - - <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"/> - - <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/> - - // ... (controller bean definitions) ... - -</beans> - - - Defining a DefaultAnnotationHandlerMapping - and/or AnnotationMethodHandlerAdapter explicitly - also makes sense if you would like to customize the mapping strategy, - e.g. specifying a custom PathMatcher or - WebBindingInitializer (see below). -
- -
- Defining a controller with - <interfacename>@Controller</interfacename> - - The @Controller annotation indicates - that a particular class serves the role of a controller. - There is no need to extend any controller base class or reference the - Servlet API. You are of course still able to reference Servlet-specific - features if you need to. - - The basic purpose of the @Controller - annotation is to act as a stereotype for the annotated class, indicating - its role. The dispatcher will scan such annotated classes for mapped - methods, detecting @RequestMapping - annotations (see the next section). - - Annotated controller beans may be defined explicitly, - using a standard Spring bean definition in the dispatcher's context. - However, the @Controller stereotype also - allows for autodetection, aligned with Spring 2.5's general support for - detecting component classes in the classpath and auto-registering bean - definitions for them. - - To enable autodetection of such annotated controllers, you have to add - component scanning to your configuration. This is easily achieved by using - the spring-context schema as shown in the following - XML snippet: - - <?xml version="1.0" encoding="UTF-8"?> -<beans xmlns="http://www.springframework.org/schema/beans" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xmlns:p="http://www.springframework.org/schema/p" - xmlns:context="http://www.springframework.org/schema/context" - xsi:schemaLocation=" - http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd - http://www.springframework.org/schema/context - http://www.springframework.org/schema/context/spring-context-3.0.xsd"> - - <context:component-scan base-package="org.springframework.samples.petclinic.web"/> - - // ... - -</beans> -
- -
- Mapping requests with - <interfacename>@RequestMapping</interfacename> - - The @RequestMapping annotation is used - to map URLs like '/editPet.do' onto an entire class or a particular handler method. - Typically the type-level annotation maps a specific request path (or path pattern) - onto a form controller, with additional method-level annotations 'narrowing' the - primary mapping for a specific HTTP method request method ("GET"/"POST") or - specific HTTP request parameters. - - - @RequestMapping at the type - level may be used for plain implementations of the - Controller interface as well. - In this case, the request processing code would follow the - traditional handleRequest signature, - while the controller's mapping would be expressed through an - @RequestMapping annotation. - This works for pre-built Controller - base classes, such as SimpleFormController, - too. - - In the following discussion, we'll focus on controllers - that are based on annotated handler methods. - - - The following is an example of a form controller from the - PetClinic sample application using this annotation: - - @Controller -@RequestMapping("/editPet.do") -@SessionAttributes("pet") -public class EditPetForm { - - private final Clinic clinic; - - @Autowired - public EditPetForm(Clinic clinic) { - this.clinic = clinic; - } - - @ModelAttribute("types") - public Collection<PetType> populatePetTypes() { - return this.clinic.getPetTypes(); - } - - @RequestMapping(method = RequestMethod.GET) - public String setupForm(@RequestParam("petId") int petId, ModelMap model) { - Pet pet = this.clinic.loadPet(petId); - model.addAttribute("pet", pet); - return "petForm"; - } - - @RequestMapping(method = RequestMethod.POST) - public String processSubmit( - @ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { - - new PetValidator().validate(pet, result); - if (result.hasErrors()) { - return "petForm"; - } - else { - this.clinic.storePet(pet); - status.setComplete(); - return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); - } - } -} - - For a traditional multi-action controller the URLs are typically - mapped directly on the methods since the controller responds to multiple - URLs. The following is an example of a multi-action controller from the - PetClinic sample application using - @RequestMapping: - - @Controller -public class ClinicController { - - private final Clinic clinic; - - @Autowired - public ClinicController(Clinic clinic) { - this.clinic = clinic; - } - - /** - * Custom handler for the welcome view. - * Note that this handler relies on the RequestToViewNameTranslator to - * determine the logical view name based on the request URL: "/welcome.do" - * -> "welcome". - */ - @RequestMapping("/welcome.do") - public void welcomeHandler() { - } - - /** - * Custom handler for displaying vets. - * Note that this handler returns a plain {@link ModelMap} object instead of - * a ModelAndView, thus leveraging convention-based model attribute names. - * It relies on the RequestToViewNameTranslator to determine the logical - * view name based on the request URL: "/vets.do" -> "vets". - * @return a ModelMap with the model attributes for the view - */ - @RequestMapping("/vets.do") - public ModelMap vetsHandler() { - return new ModelMap(this.clinic.getVets()); - } - - /** - * Custom handler for displaying an owner. - * Note that this handler returns a plain {@link ModelMap} object instead of - * a ModelAndView, thus leveraging convention-based model attribute names. - * It relies on the RequestToViewNameTranslator to determine the logical - * view name based on the request URL: "/owner.do" -> "owner". - * @param ownerId the ID of the owner to display - * @return a ModelMap with the model attributes for the view - */ - @RequestMapping("/owner.do") - public ModelMap ownerHandler(@RequestParam("ownerId") int ownerId) { - return new ModelMap(this.clinic.loadOwner(ownerId)); - } -} - -
- Advanced <interfacename>@RequestMapping</interfacename> options - - Ant-style path patterns are supported (e.g. "/myPath/*.do"). - At the method level, relative paths (e.g. "edit.do") are supported - within the primary mapping expressed at the type level. - - The handler method names are taken into account for narrowing - if no path was specified explicitly, according to the specified - org.springframework.web.servlet.mvc.multiaction.MethodNameResolver - (by default an - org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver). - Note that this only applies in case of ambiguous annotation mappings - that do not specify a path mapping explicitly. In other words, - the method name is only used for narrowing among a set of matching - methods; it does not constitute a primary path mapping itself. - - If you have a single default method (without explicit path mapping), - then all requests without a more specific mapped method found will - be dispatched to it. If you have multiple such default methods, then - the method name will be taken into account for choosing between them. - - Path mappings can be narrowed through parameter conditions: - a sequence of "myParam=myValue" style expressions, with a request only - mapped if each such parameter is found to have the given value. - "myParam" style expressions are also supported, with such parameters - having to be present in the request (allowed to have any value). - Finally, "!myParam" style expressions indicate that the specified parameter - is not supposed to be present in the request. -
-
- -
- Supported handler method arguments and return types - - Handler methods which are annotated with - @RequestMapping are allowed to have very flexible - signatures. They may have arguments of the following types, in arbitrary - order (except for validation results, which need to follow right after - the corresponding command object, if desired): - - - - Request and/or response objects (Servlet API). You may choose any - specific request/response type, e.g. ServletRequest / - HttpServletRequest. - - - - Session object (Servlet API): of type HttpSession. - An argument of this type will enforce the presence of a corresponding session. - As a consequence, such an argument will never be null. - Note that session access may not be thread-safe, in particular - in a Servlet environment: Consider switching the - AnnotationMethodHandlerAdapter's - "synchronizeOnSession" flag to "true" if multiple requests are allowed - to access a session concurrently. - - - - org.springframework.web.context.request.WebRequest - or org.springframework.web.context.request.NativeWebRequest. - Allows for generic request parameter access as well as request/session - attribute access, without ties to the native Servlet/Portlet API. - - - - java.util.Locale for the current request - locale (determined by the most specific locale resolver available, - i.e. the configured LocaleResolver - in a Servlet environment). - - - - java.io.InputStream / - java.io.Reader for access to the request's content. - This will be the raw InputStream/Reader as exposed by the Servlet API. - - - - java.io.OutputStream / - java.io.Writer for generating the response's content. - This will be the raw OutputStream/Writer as exposed by the Servlet API. - - - - @RequestParam annotated parameters - for access to specific Servlet request parameters. Parameter values - will be converted to the declared method argument type. - - - - java.util.Map / - org.springframework.ui.Model / - org.springframework.ui.ModelMap for - enriching the implicit model that will be exposed to the web view. - - - - Command/form objects to bind parameters to: as bean - properties or fields, with customizable type conversion, depending - on @InitBinder methods and/or the - HandlerAdapter configuration - see the - "webBindingInitializer" property on - AnnotationMethodHandlerAdapter. Such - command objects along with their validation results will be - exposed as model attributes, by default using the non-qualified - command class name in property notation (e.g. "orderAddress" for - type "mypackage.OrderAddress"). Specify a parameter-level - ModelAttribute annotation for declaring a - specific model attribute name. - - - - org.springframework.validation.Errors / - org.springframework.validation.BindingResult - validation results for a preceding command/form object (the - immediate preceding argument). - - - - org.springframework.web.bind.support.SessionStatus - status handle for marking form processing as complete (triggering - the cleanup of session attributes that have been indicated by the - @SessionAttributes annotation at the - handler type level). - - - - The following return types are supported for handler methods: - - - - A ModelAndView object, with the model implicitly - enriched with command objects and the results of @ModelAttribute - annotated reference data accessor methods. - - - - A Model object, with the view name implicitly - determined through a RequestToViewNameTranslator - and the model implicitly enriched with command objects and the results of - @ModelAttribute annotated reference data accessor methods. - - - - A Map object for exposing a model, with the view name - implicitly determined through a RequestToViewNameTranslator - and the model implicitly enriched with command objects and the results of - @ModelAttribute annotated reference data accessor methods. - - - - A View object, with the model implicitly - determined through command objects and @ModelAttribute - annotated reference data accessor methods. The handler method may also - programmatically enrich the model by declaring a Model - argument (see above). - - - - A String value which is interpreted as view name, - with the model implicitly determined through command objects and - @ModelAttribute annotated reference data accessor methods. - The handler method may also programmatically enrich the model by declaring a - Model argument (see above). - - - - void if the method handles the response itself - (by writing the response content directly, declaring an argument of type - ServletResponse / - HttpServletResponse for that purpose) - or if the view name is supposed to be implicitly determined through a - RequestToViewNameTranslator - (not declaring a response argument in the handler method signature). - - - - Any other return type will be considered as single model attribute - to be exposed to the view, using the attribute name specified through - @ModelAttribute at the method level (or the default - attribute name based on the return type's class name otherwise). The model - will be implicitly enriched with command objects and the results of - @ModelAttribute annotated reference data accessor methods. - - -
- -
- Binding request parameters to method parameters with - <classname>@RequestParam</classname> - - The @RequestParam annotation is used to - bind request parameters to a method parameter in your controller. - - The following code snippet from the PetClinic sample application - shows the usage: - - @Controller -@RequestMapping("/editPet.do") -@SessionAttributes("pet") -public class EditPetForm { - - // ... - - @RequestMapping(method = RequestMethod.GET) - public String setupForm(@RequestParam("petId") int petId, ModelMap model) { - Pet pet = this.clinic.loadPet(petId); - model.addAttribute("pet", pet); - return "petForm"; - } - - // ... - - - Parameters using this annotation are required by default, but you - can specify that a parameter is optional by setting - @RequestParam's - required attribute to false (e.g., - @RequestParam(value="id", required="false")). -
- -
- Providing a link to data from the model with - <classname>@ModelAttribute</classname> - - @ModelAttribute has two usage scenarios in - controllers. When placed on a method parameter, - @ModelAttribute is used to map a model attribute - to the specific, annotated method parameter (see the - processSubmit() method below). This is how the - controller gets a reference to the object holding the data entered in - the form. In addition, the parameter can be declared as the specific - type of the form backing object rather than as a generic - java.lang.Object, thus increasing type - safety. - - @ModelAttribute is also used at the method - level to provide reference data for the model (see - the populatePetTypes() method below). For this usage - the method signature can contain the same types as documented above for - the @RequestMapping annotation. - - Note: @ModelAttribute - annotated methods will be executed before the - chosen @RequestMapping annotated handler method. - They effectively pre-populate the implicit model with specific attributes, - often loaded from a database. Such an attribute can then already be - accessed through @ModelAttribute annotated - handler method parameters in the chosen handler method, potentially - with binding and validation applied to it. - - The following code snippet shows these two usages of this - annotation: - - @Controller -@RequestMapping("/editPet.do") -@SessionAttributes("pet") -public class EditPetForm { - - // ... - - @ModelAttribute("types") - public Collection<PetType> populatePetTypes() { - return this.clinic.getPetTypes(); - } - - @RequestMapping(method = RequestMethod.POST) - public String processSubmit( - @ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { - - new PetValidator().validate(pet, result); - if (result.hasErrors()) { - return "petForm"; - } - else { - this.clinic.storePet(pet); - status.setComplete(); - return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); - } - } -} -
- -
- Specifying attributes to store in a Session with - <classname>@SessionAttributes</classname> - - The type-level @SessionAttributes - annotation declares session attributes used by a specific handler. This - will typically list the names of model attributes which should be - transparently stored in the session or some conversational storage, - serving as form-backing beans between subsequent requests. - - The following code snippet shows the usage of this - annotation: - - @Controller -@RequestMapping("/editPet.do") -@SessionAttributes("pet") -public class EditPetForm { - // ... -} - -
-
- Mapping cookie values with the @CookieValue annotation - - The @CookieValue annotation allows a method parameter to be bound to the value of an HTTP cookie. - - - Let us consider that the following cookie has been received with an http request: - JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84 - The following code sample allows you to easily get the value of the "JSESSIONID"cookie: - @RequestMapping("/displayHeaderInfo.do") -public void displayHeaderInfo(@CookieValue("JSESSIONID") String cookie) { - - //... - -} - This annotation is supported for annotated handler methods in Servlet and Portlet environments. -
- -
- Mapping request header attributes with the @RequestHeader annotation - - The @RequestHeader annotation allows a method parameter to be bound to a request header. - - - Here is a request header sample: - - The following code sample allows you to easily get the value of the "Accept-Encoding" and "Keep-Alive" headers: - @RequestMapping("/displayHeaderInfo.do") - public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, - @RequestHeader("Keep-Alive") long keepAlive) { - - //... - -} - This annotation is supported for annotated handler methods in Servlet and Portlet environments. -
- - -
- Customizing <classname>WebDataBinder</classname> - initialization - - To customize request parameter binding with PropertyEditors, etc. - via Spring's WebDataBinder, you can either use - @InitBinder-annotated methods within your - controller or externalize your configuration by providing a custom - WebBindingInitializer. - -
- Customizing data binding with - <interfacename>@InitBinder</interfacename> - - Annotating controller methods with - @InitBinder allows you to configure web - data binding directly within your controller class. - @InitBinder identifies methods which - initialize the WebDataBinder which will be used - for populating command and form object arguments of annotated handler - methods. - - Such init-binder methods support all arguments that - @RequestMapping supports, except for - command/form objects and corresponding validation result objects. - Init-binder methods must not have a return value. Thus, they are - usually declared as void. Typical arguments include - WebDataBinder in combination with - WebRequest or - java.util.Locale, allowing code to register - context-specific editors. - - The following example demonstrates the use of - @InitBinder for configuring a - CustomDateEditor for all - java.util.Date form properties. - - @Controller -public class MyFormController { - - @InitBinder - public void initBinder(WebDataBinder binder) { - SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); - dateFormat.setLenient(false); - binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); - } - - // ... -} -
- -
- Configuring a custom - <interfacename>WebBindingInitializer</interfacename> - - To externalize data binding initialization, you can provide a - custom implementation of the - WebBindingInitializer interface, which - you then enable by supplying a custom bean configuration for an - AnnotationMethodHandlerAdapter, thus overriding - the default configuration. - - The following example from the PetClinic application shows a - configuration using a custom implementation of the - WebBindingInitializer interface, - org.springframework.samples.petclinic.web.ClinicBindingInitializer, - which configures PropertyEditors required by several of the PetClinic - controllers. - - <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> - <property name="cacheSeconds" value="0" /> - <property name="webBindingInitializer"> - <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> - </property> -</bean> - -
-
-
-
Further Resources @@ -3373,15 +3159,15 @@ public class MyFormController { MVC-based application using a step-by-step approach. This tutorial is available in the 'docs' directory of the Spring distribution. An online version can also be found on the Spring Framework website. + url="http://springframework.org/">Spring Framework + website. - The book entitled Expert Spring Web MVC and - Web Flow by Seth Ladd and others (published by Apress) is an + The book entitled Expert Spring Web MVC and Web + Flow by Seth Ladd and others (published by Apress) is an excellent hardcopy source of Spring Web MVC goodness.
- - \ No newline at end of file + diff --git a/spring-framework-reference/src/remoting.xml b/spring-framework-reference/src/remoting.xml index 6570e28d35e..bfb70195c04 100644 --- a/spring-framework-reference/src/remoting.xml +++ b/spring-framework-reference/src/remoting.xml @@ -68,7 +68,7 @@ While discussing the remoting capabilities of Spring, we'll use the following domain model and corresponding services: - public class Account implements Serializable{ private String name; @@ -79,21 +79,21 @@ public void setName(String name) { this.name = name; } -}]]> +}
- public interface AccountService { public void insertAccount(Account account); - public List getAccounts(String name); -}]]> + public List<Account> getAccounts(String name); +} - public interface RemoteAccountService extends Remote { public void insertAccount(Account account) throws RemoteException; - public List getAccounts(String name) throws RemoteException; -}]]> + public List<Account> getAccounts(String name) throws RemoteException; +} // the implementation doing nothing at the moment public class AccountServiceImpl implements AccountService { @@ -174,7 +174,7 @@ public class AccountServiceImpl implements AccountService { Our client is a simple object using the AccountService to manage accounts: - public class SimpleObject { private AccountService accountService; @@ -184,20 +184,20 @@ public class AccountServiceImpl implements AccountService { // additional methods using the accountService -}]]> +} To link in the service on the client, we'll create a separate Spring container, containing the simple object and the service linking configuration bits: - - - + <bean class="example.SimpleObject"> + <property name="accountService" ref="accountService"/> +</bean> - - - -]]> +<bean id="accountService" class="org.springframework.remoting.rmi.RmiProxyFactoryBean"> + <property name="serviceUrl" value="rmi://HOST:1199/AccountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean> That's all we need to do to support the remote account service on the client. Spring will transparently create an invoker and remotely @@ -225,16 +225,16 @@ public class AccountServiceImpl implements AccountService { application (this an excerpt from 'web.xml'): - - remoting - org.springframework.web.servlet.DispatcherServlet - 1 - + <servlet> + <servlet-name>remoting</servlet-name> + <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> + <load-on-startup>1</load-on-startup> +</servlet> - - remoting - /remoting/* -]]> +<servlet-mapping> + <servlet-name>remoting</servlet-name> + <url-pattern>/remoting/*</url-pattern> +</servlet-mapping> You're probably familiar with Spring's DispatcherServlet principles and if so, you know @@ -284,10 +284,10 @@ public class AccountServiceImpl implements AccountService { context (e.g. in 'WEB-INF/applicationContext.xml'): - - - -]]> + <bean name="accountExporter" class="org.springframework.remoting.caucho.HessianServiceExporter"> + <property name="service" ref="accountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean> In the latter case, define a corresponding servlet for this exporter in 'web.xml', with the same end result: @@ -295,15 +295,15 @@ public class AccountServiceImpl implements AccountService { /remoting/AccountService. Note that the servlet name needs to match the bean name of the target exporter. - - accountExporter - org.springframework.web.context.support.HttpRequestHandlerServlet - + <servlet> + <servlet-name>accountExporter</servlet-name> + <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class> +</servlet> - - accountExporter - /remoting/AccountService -]]> +<servlet-mapping> + <servlet-name>accountExporter</servlet-name> + <url-pattern>/remoting/AccountService</url-pattern> +</servlet-mapping>
@@ -316,14 +316,14 @@ public class AccountServiceImpl implements AccountService { SimpleObject is using the AccountService to manage accounts: - - - + <bean class="example.SimpleObject"> + <property name="accountService" ref="accountService"/> +</bean> - - - -]]> +<bean id="accountService" class="org.springframework.remoting.caucho.HessianProxyFactoryBean"> + <property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean>
@@ -349,14 +349,14 @@ public class AccountServiceImpl implements AccountService { Hessian/BurlapProxyFactoryBean level (similar to a JDBC DataSource). - - - + <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> + <property name="interceptors" ref="authorizationInterceptor"/> +</bean> - - -]]> +<bean id="authorizationInterceptor" + class="org.springframework.web.servlet.handler.UserRoleAuthorizationInterceptor"> + <property name="authorizedRoles" value="administrator,operator"/> +</bean> This an example where we explicitly mention the BeanNameUrlHandlerMapping and set an interceptor @@ -405,11 +405,11 @@ public class AccountServiceImpl implements AccountService { following configuration needs to be in place in the dispatcher's application context: - - - - -]]> + <bean name="/AccountService" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> + <property name="service" ref="accountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean> + Such an exporter definition will be exposed through the DispatcherServlet's standard mapping facilities, @@ -420,46 +420,46 @@ public class AccountServiceImpl implements AccountService { application context (e.g. in 'WEB-INF/applicationContext.xml'): - - - -]]> + <bean name="accountExporter" class="org.springframework.remoting.httpinvoker.HttpInvokerServiceExporter"> + <property name="service" ref="accountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean> In addition, define a corresponding servlet for this exporter in 'web.xml', with the servlet name matching the bean name of the target exporter: - - accountExporter - org.springframework.web.context.support.HttpRequestHandlerServlet - + <servlet> + <servlet-name>accountExporter</servlet-name> + <servlet-class>org.springframework.web.context.support.HttpRequestHandlerServlet</servlet-class> +</servlet> - - accountExporter - /remoting/AccountService -]]> +<servlet-mapping> + <servlet-name>accountExporter</servlet-name> + <url-pattern>/remoting/AccountService</url-pattern> +</servlet-mapping> f you are running outside of a servlet container and are using Sun's Java 6, then you can use the built-in HTTP server implementation. You can configure the SimpleHttpServerFactoryBean together with a SimpleHttpInvokerServiceExporter as is shown in this example: - - - - + <bean name="accountExporter" + class="org.springframework.remoting.httpinvoker.SimpleHttpInvokerServiceExporter"> + <property name="service" ref="accountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean> - - - - - - - - -]]> +<bean id="httpServer" + class="org.springframework.remoting.support.SimpleHttpServerFactoryBean"> + <property name="contexts"> + <util:map> + <entry key="/remoting/AccountService" value-ref="accountExporter"/> + </util:map> + </property> + <property name="port" value="8080" /> +</bean> +
@@ -470,11 +470,11 @@ public class AccountServiceImpl implements AccountService { will be able to translate your calls to HTTP POST requests to the URL pointing to the exported service. - - - - -]]> + <bean id="httpInvokerProxy" class="org.springframework.remoting.httpinvoker.HttpInvokerProxyFactoryBean"> + <property name="serviceUrl" value="http://remotehost:8080/remoting/AccountService"/> + <property name="serviceInterface" value="example.AccountService"/> +</bean> + As mentioned before, you can choose what HTTP client you want to use. By default, the HttpInvokerProxy uses the @@ -482,10 +482,10 @@ public class AccountServiceImpl implements AccountService { HttpClient by setting the httpInvokerRequestExecutor property: - - - -]]> + <property name="httpInvokerRequestExecutor"> + <bean class="org.springframework.remoting.httpinvoker.CommonsHttpInvokerRequestExecutor"/> +</property> +
@@ -609,13 +609,13 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re support for web services requiring little coding efforts - most of the setup is done in the Spring configuration file as usual: - - - - - - -]]> + <bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean"> + <property name="serviceInterface" value="example.RemoteAccountService"/> + <property name="wsdlDocumentUrl" value="http://localhost:8080/account/services/accountService?WSDL"/> + <property name="namespaceUri" value="http://localhost:8080/account/services/accountService"/> + <property name="serviceName" value="AccountService"/> + <property name="portName" value="AccountPort"/> +</bean> Where serviceInterface is our remote business interface the clients will use. wsdlDocumentUrl is @@ -631,10 +631,10 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re RemoteAccountService interface. We can wire this up in Spring: - + <bean id="client" class="example.AccountClientImpl"> ... - -]]> + <property name="service" ref="accountWebService"/> +</bean> From the client code we can access the web service just as if it was a normal class, except that it throws @@ -665,18 +665,18 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re RemoteException. This requires that we provide a non-RMI interface also. Our configuration is now: - - - + <bean id="accountWebService" class="org.springframework.remoting.jaxrpc.JaxRpcPortProxyFactoryBean"> + <property name="serviceInterface" value="example.AccountService"/> + <property name="portInterface" value="example.RemoteAccountService"/> ... -]]> +</bean> Where serviceInterface is changed to our non RMI interface. Our RMI interface is now defined using the property portInterface. Our client code can now avoid handling java.rmi.RemoteException: - public class AccountClientImpl { private AccountService service; @@ -687,7 +687,7 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re public void foo() { service.insertAccount(...); } -}]]> +} Note that you can also drop the "portInterface" part and specify a plain business interface as "serviceInterface". In this case, @@ -717,7 +717,7 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re We will use Axis to register bean mappings on the client side. To do this we need to register the bean mappings programmatically: - public class AxisPortProxyFactoryBean extends JaxRpcPortProxyFactoryBean { protected void postProcessJaxRpcService(Service service) { TypeMappingRegistry registry = service.getTypeMappingRegistry(); @@ -732,7 +732,7 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re new BeanSerializerFactory(type, qName), new BeanDeserializerFactory(type, qName)); } -}]]> +}
@@ -747,7 +747,7 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re javax.rpc.xml.handler.GenericHandler that we will extend: - public class AccountHandler extends GenericHandler { public QName[] getHeaders() { return null; @@ -766,7 +766,7 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re } return true; } -}]]> +} What we need to do now is to register our AccountHandler to JAX-RPC Service so it would invoke @@ -777,7 +777,7 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re override the postProcessJaxRpcService(..) method that is designed for this: - public class AccountHandlerJaxRpcPortProxyFactoryBean extends JaxRpcPortProxyFactoryBean { protected void postProcessJaxRpcService(Service service) { QName port = new QName(this.getNamespaceUri(), this.getPortName()); @@ -785,14 +785,14 @@ public class AccountServiceEndpoint extends ServletEndpointSupport implements Re list.add(new HandlerInfo(AccountHandler.class, null, null)); logger.info("Registered JAX-RPC AccountHandler on port " + port); } -}]]> +} The last thing we must remember to do is to change the Spring configuration to use our factory bean: - + <bean id="accountWebService" class="example.AccountHandlerJaxRpcPortProxyFactoryBean"> ... -]]> +</bean>
@@ -870,15 +870,15 @@ public class AccountServiceEndpoint extends SpringBeanAutowiringSupport { injection through @Autowired will work as well. - - - + <bean class="org.springframework.remoting.jaxws.SimpleJaxWsServiceExporter"> + <property name="baseAddress" value="http://localhost:8080/"/> +</bean> - +<bean id="accountServiceEndpoint" class="example.AccountServiceEndpoint"> ... - +</bean> -...]]> +... The AccountServiceEndpoint may derive from Spring's SpringBeanAutowiringSupport but doesn't @@ -887,7 +887,7 @@ public class AccountServiceEndpoint extends SpringBeanAutowiringSupport { any superclass declared - and Spring's @Autowired configuration annotation still being honored: - @WebService(serviceName="AccountService") public class AccountServiceEndpoint { @Autowired @@ -899,10 +899,10 @@ public class AccountServiceEndpoint { } @WebMethod - public List getAccounts(String name) { + public List<Account> getAccounts(String name) { return biz.getAccounts(name); } -}]]> +}
@@ -945,13 +945,13 @@ public class AccountServiceEndpoint { a proxy for the AccountService endpoint (again): - - - - - - -]]> + <bean id="accountWebService" class="org.springframework.remoting.jaxws.JaxWsPortProxyFactoryBean"> + <property name="serviceInterface" value="example.AccountService"/> + <property name="wsdlDocumentUrl" value="http://localhost:8888/AccountServiceEndpoint?WSDL"/> + <property name="namespaceUri" value="http://example/"/> + <property name="serviceName" value="AccountService"/> + <property name="portName" value="AccountServiceEndpointPort"/> +</bean> Where serviceInterface is our business interface the clients will use. wsdlDocumentUrl is @@ -966,15 +966,15 @@ public class AccountServiceEndpoint { factory for it that will expose it as AccountService interface. We can wire this up in Spring: - + <bean id="client" class="example.AccountClientImpl"> ... - -]]> + <property name="service" ref="accountWebService"/> +</bean> From the client code we can access the web service just as if it was a normal class: - public class AccountClientImpl { private AccountService service; @@ -985,7 +985,7 @@ public class AccountServiceEndpoint { public void foo() { service.insertAccount(...); } -}]]> +} NOTE: The above is slightly simplified in that JAX-WS requires endpoint interfaces and implementation classes to @@ -1008,10 +1008,10 @@ public class AccountServiceEndpoint { WebApplicationContext containing the services you will be exposing: - - xfire - org.springframework.web.servlet.DispatcherServlet -]]> + <servlet> + <servlet-name>xfire</servlet-name> + <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> +</servlet> You also have to link in the XFire configuration. This is done by adding a context file to the contextConfigLocations @@ -1019,14 +1019,14 @@ public class AccountServiceEndpoint { ContextLoaderListener (or ContextLoaderServlet for that matter). - - contextConfigLocation - classpath:org/codehaus/xfire/spring/xfire.xml - + <context-param> + <param-name>contextConfigLocation</param-name> + <param-value>classpath:org/codehaus/xfire/spring/xfire.xml</param-value> +</context-param> - - org.springframework.web.context.ContextLoaderListener -]]> +<listener> + <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> +</listener> After you added a servlet mapping (mapping /* to the XFire servlet declared above) you only have to add one extra bean @@ -1068,43 +1068,43 @@ public class AccountServiceEndpoint { The following interface is used on both the server and the client side. - package com.foo; public interface CheckingAccountService { public void cancelAccount(Long accountId); -}]]> +} The following simple implementation of the above interface is used on the server-side. - package com.foo; public class SimpleCheckingAccountService implements CheckingAccountService { public void cancelAccount(Long accountId) { System.out.println("Cancelling account [" + accountId + "]"); } -}]]> +} This configuration file contains the JMS-infrastructure beans that are shared on both the client and server. - -<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - - - + <bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"> + <property name="brokerURL" value="tcp://ep-t43:61616"/> + </bean> - - - + <bean id="queue" class="org.apache.activemq.command.ActiveMQQueue"> + <constructor-arg value="mmm"/> + </bean> -]]> +</beans>
Server-side configuration @@ -1112,30 +1112,30 @@ public class SimpleCheckingAccountService implements CheckingAccountService { On the server, you just need to expose the service object using the JmsInvokerServiceExporter. - -<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - - - - - - + <bean id="checkingAccountService" + class="org.springframework.jms.remoting.JmsInvokerServiceExporter"> + <property name="serviceInterface" value="com.foo.CheckingAccountService"/> + <property name="service"> + <bean class="com.foo.SimpleCheckingAccountService"/> + </property> + </bean> - - - - - - + <bean class="org.springframework.jms.listener.SimpleMessageListenerContainer"> + <property name="connectionFactory" ref="connectionFactory"/> + <property name="destination" ref="queue"/> + <property name="concurrentConsumers" value="3"/> + <property name="messageListener" ref="checkingAccountService"/> + </bean> -]]> +</beans> - package com.foo; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -1144,7 +1144,7 @@ public class Server { public static void main(String[] args) throws Exception { new ClassPathXmlApplicationContext(new String[]{"com/foo/server.xml", "com/foo/jms.xml"}); } -}]]> +}
@@ -1157,22 +1157,22 @@ public class Server { injected into other client side objects, and the proxy will take care of forwarding the call to the server-side object via JMS. - -<?xml version="1.0" encoding="UTF-8"?> +<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans - http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> + http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> - - - - - + <bean id="checkingAccountService" + class="org.springframework.jms.remoting.JmsInvokerProxyFactoryBean"> + <property name="serviceInterface" value="com.foo.CheckingAccountService"/> + <property name="connectionFactory" ref="connectionFactory"/> + <property name="queue" ref="queue"/> + </bean> -]]> +</beans> - package com.foo; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; @@ -1185,7 +1185,7 @@ public class Client { CheckingAccountService service = (CheckingAccountService) ctx.getBean("checkingAccountService"); service.cancelAccount(new Long(10)); } -}]]> +}
You may also wish to investigate the support provided by the
-
\ No newline at end of file + +
+ Accessing RESTful services on the Client + + The RestTemplate is the core class for + client-side access to RESTful services. It is conceptually similar to + other template classes in Spring, such as + JdbcTemplate and JmsTemplate + and other template classes found in other Spring portfolio projects. + RestTemplate's behavior is customized by providing + callback methods and configuring the + HttpMessageConverter used to marshal + objects into the HTTP request body and to unmarshall any response back + into an object. As it is common to use XML as a message format, Spring + provides a MarshallingHttpMessageConverter that + uses the Object-to-XML framework that is part of the + org.springframework.oxm package. This gives you a + wide range of choices of XML to Object mapping technologies to choose + from. + + This section describes how to use the + RestTemplate and its associated + HttpMessageConverters. + +
+ RestTemplate + + Invoking RESTful services in Java is typically done using a helper + class such as Jakarta Commons HttpClient. For + common REST operations this approach is too low level as shown + below. + + String uri = "http://example.com/hotels/1/bookings"; + +PostMethod post = new PostMethod(uri); +String request = // create booking request content +post.setRequestEntity(new StringRequestEntity(request)); + +httpClient.executeMethod(post); + +if (HttpStatus.SC_CREATED == post.getStatusCode()) { + Header location = post.getRequestHeader("Location"); + if (location != null) { + System.out.println("Created new booking at :" + location.getValue()); + } +} + + RestTemplate provides higher level methods that correspond to each + of the six main HTTP methods that make invoking many RESTful services a + one-liner and enforce REST best practices. + + + Overview of RestTemplate methods + + + + + HTTP Method + + RestTemplate + Method + + + + DELETE + + delete(String + url, String… urlVariables) + + + + GET + + getForObject(String + url, Class<T> responseType, String… + urlVariables) + + + + HEAD + + headForHeaders(String + url, String… urlVariables) + + + + OPTIONS + + optionsForAllow(String + url, String… urlVariables) + + + + POST + + postForLocation(String + url, Object request, String… urlVariables) + + + + PUT + + put(String + url, Object request, String…urlVariables) + + + +
+ + The names of RestTemplate methods follow a + naming convention, the first part indicates what HTTP method is being + invoked and the second part indicates what is returned. For example, the + method getForObject will perform a GET, convert + the HTTP response into an object type of your choice and return that + object. The method postForLocation will do a + POST, converting the given object into a HTTP request and return the + response HTTP Location header where the newly created object can be + found In case of an exception processing the HTTP request, an exception + of the type RestClientException will be + thrown. + + Objects passed to and returned from these methods are converted to + and from HTTP messages by + HttpMessageConverter instances. + Converters for the main mime types are registered by default, but you + can also write your own converter and register it via the + messageConverters bean property. The default + converter instances registered with the template are + ByteArrayHttpMessageConverter, + StringHttpMessageConverter, + FormHttpMessageConverter and + SourceHttpMessageConverter. You can override + these defaults using the messageConverters bean + property as would be required if using the + MarshallingHttpMessageConverter. + + Each method takes URI template arguments in two forms, either as a + String variable length argument or a + Map<String,String>. For example, + + String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", + String.class,"42", "21"); + + + using variable length arguments and + + Map<String, String> vars = Collections.singletonMap("hotel", "42"); +String result = + restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); + + + using a Map<String,String>. + + To create an instance of RestTemplate you + can simply call the default constructor. This will use standard Java + classes from the java.net package as the underlying + implementation to create HTTP requests. This can be overridden by + specifying an implementation of + ClientHttpRequestFactory. Spring provides + the implementation + CommonsClientHttpRequestFactory that uses the + Jakarta Commons HttpClient to create requests. + CommonsClientHttpRequestFactory is configured + using an instance of + org.apache.commons.httpclient.HttpClient which + can in turn be configured with credentials information or connection + pooling functionality. + + The previous example using Jakarta Commons + HttpClient directly rewritten to use the + RestTemplate is shown below + + uri = "http://example.com/hotels/{id}/bookings"; + +RestTemplate template = new RestTemplate(); + +Booking booking = // create booking object + +URI location = template.postForLocation(uri, booking, "1"); + + + The general callback interface is + RequestCallback and is called when the + execute method is invoked. + + public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, + ResponseExtractor<T> responseExtractor, + String... urlVariables) + + +// also has an overload with urlVariables as a Map<String, String>. + + The RequestCallback interface is + defined as + + public interface RequestCallback { + void doWithRequest(ClientHttpRequest request) throws IOException; +} + + and allows you to manipulate the request headers and write to the + request body. When using the execute method you do not have to worry + about any resource management, the template will always close the + request and handle any errors. Refer to the API documentation for more + information on using the execute method and the meaning of its other + method arguments. +
+ +
+ HTTP Message Conversion + + Objects passed to and returned from the methods + getForObject, + postForLocation, and + put are converted to HTTP requests and from + HTTP responses by HttpMessageConverters. + The HttpMessageConverter interface is + shown below to give you a better feel for its functionality + + public interface HttpMessageConverter<T> { + + // Indicate whether the given class is supported by this converter. + boolean supports(Class<? extends T> clazz); + + // Return the list of MediaType objects supported by this converter. + List<MediaType> getSupportedMediaTypes(); + + // Read an object of the given type form the given input message, and returns it. + T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, + HttpMessageNotReadableException; + + // Write an given object to the given output message. + void write(T t, HttpOutputMessage outputMessage) throws IOException, + HttpMessageNotWritableException; + + } + + Concrete implementations for the main media (mime) types are + provided in the framework and are registered by default with the + RestTemplate on the client-side and with + AnnotationMethodHandlerAdapter on the + server-side. + + The implementations of + HttpMessageConverters are described in the + following sections. For all converters a default media type is used but + can be overridden by setting the + supportedMediaTypes bean property + +
+ StringHttpMessageConverter + + An HttpMessageConverter + implementation that can read and write Strings from the HTTP request + and response. By default, this converter supports all text media types + (text/*), and writes with a + Content-Type of + text/plain. +
+ +
+ FormHttpMessageConverter + + An HttpMessageConverter + implementation that can read and write form data from the HTTP request + and response. By default, this converter reads and writes the media + type application/x-www-form-urlencoded. Form data + is read from and written into a MultiValueMap<String, + String>. +
+ +
+ ByteArrayMessageConverter + + An HttpMessageConverter + implementation that can read and write byte arrays from the HTTP + request and response. By default, this converter supports all media + types (*/*), and writes with a + Content-Type of + application/octet-stream. This can be overridden by + setting the supportedMediaTypes property, and + overriding getContentType(byte[]). +
+ +
+ MarshallingHttpMessageConverter + + An HttpMessageConverter + implementation that can read and write XML using Spring's + Marshaller and + Unmarshaller abstractions from the + org.springframework.oxm package. This converter + requires a Marshaller and + Unmarshaller before it can be used. + These can be injected via constructor or bean properties. By default + this converter supports (text/xml) and + (application/xml). +
+ +
+ SourceHttpMessageConverter + + An HttpMessageConverter + implementation that can read and write + javax.xml.transform.Source from the HTTP + request and response. Only DOMSource, + SAXSource, and + StreamSource are supported. By default, this + converter supports (text/xml) and + (application/xml). +
+ +
+ BufferedImageHttpMessageConverter + + An HttpMessageConverter + implementation that can read and write + java.awt.image.BufferedImage from the HTTP + request and response. This converter reads and writes the media type + supported by the Java I/O API. +
+
+
+
diff --git a/spring-framework-reference/src/rest.xml b/spring-framework-reference/src/rest.xml index e6d33dadcc9..91d9f837ecc 100644 --- a/spring-framework-reference/src/rest.xml +++ b/spring-framework-reference/src/rest.xml @@ -38,368 +38,6 @@ configuration to understand the general programming model. -
- Creating RESTful services - - Spring's annotation-based MVC framework serves as the basis for - creating RESTful Web Services. As such, you configure your servlet - container as you would for a Spring MVC application using Spring's DispatcherServlet. - -
- URI Templates - - RESTful services use URIs to name resources. To facilitate - accessing the information contained in a URI, its structure follows - conventions so that it can easily be described in a parameterized form. - The proposed - RFC for URI Templates defines how an URI is parameterized. For - example, the URI Template - - http://www.example.com/users/{userid} - - contains the variable 'userid'. If we assign the variable the - value "fred", then 'expanding' the URI Template gives. - - http://www.example.com/users/fred - - When processing a request the URI can be compared to an expected - URI Template in order to extract a collection of variables. - - Spring uses the @RequestMapping method - annotation to define the URI Template for the request. The - @PathVariable annotation is used to extract the - value of the template variables and assign their value to a method - variable. A Spring controller method to process above example is shown - below; - - @RequestMapping(value="/users/{userId}", method=RequestMethod.GET) -public String getUser(@PathVariable String userId) { - // implementation omitted... -} - - The request http://www.example.com/users/fred - will bind the userId method parameter to the String value 'fred'. - -
- Mapping RESTful URLs with the @PathVariable annotation - - The @PathVariable method parameter - annotation is used to indicate that a method parameter should be bound - to the value of a URI template variable. - - The following code snippet shows the use of a single - @PathVariable in a controller method: - - @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) -public String findOwner(@PathVariable String ownerId, Model model) { - Owner owner = ownerService.findOwner(ownerId); - model.addAttribute("owner", owner); - return "displayOwner"; -} - - - The URI Template "/owners/{ownerId}" - specifies the variable name ownerId. When the controller handles this - request, the value of ownerId is set the value in the request URI. For - example, when a request comes in for /owners/fred, the value 'fred' is - bound to the method parameter String - ownerId. - - The matching of method parameter names to URI Template variable - names can only be done if your code is compiled with debugging - enabled. If you do have not debugging enabled, you must specify the - name of the URI Template variable name to bind to in the @PathVariable - annotation. For example - - @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) -public String findOwner(@PathVariable("ownerId") String ownerId, Model model) { - // implementation omitted -} - - - The name of the method parameter does not matter in this case, - so you may also use a controller method with the signature shown - below - - @RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET) -public String findOwner(@PathVariable("ownerId") String theOwner, Model model) { - // implementation omitted -} - - Multiple @PathVariable annotations can be used to bind to - multiple URI Template variables as shown below: - - @RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET) -public String findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { - Owner owner = ownerService.findOwner(ownderId); - Pet pet = owner.getPet(petId); - model.addAttribute("pet", pet); - return "displayPet"; -} - - - The following code snippet shows the use of path variables on a - relative path - - @Controller -@RequestMapping("/owners/{ownerId}/**") -public class RelativePathUriTemplateController { - - @RequestMapping("/pets/{petId}") - public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) { - // implementation omitted - } -} - - - - method parameters that are decorated with the - @PathVariable annotation can be of - any simple type such as int, long, - Date... Spring automatically converts to the appropriate type and - throws a TypeMismatchException if the type is - not correct. - -
- -
- Mapping the request body with the @RequestBody - annotation - - The @RequestBody method parameter - annotation is used to indicate that a method parameter should be bound - to the value of the HTTP request body. For example, - - @RequestMapping(value = "/something", method = RequestMethod.PUT) -public void handle(@RequestBody String body, Writer writer) throws IOException { - writer.write(body); -} - - The conversion of the request body to the method argument is - done using a HttpMessageConverter. - HttpMessageConverter is responsible for - converting from the HTTP request message to an object and converting - from an object to the HTTP response body. - DispatcherServlet supports annotation based - processing using the - DefaultAnnotationHandlerMapping and - AnnotationMethodHandlerAdapter. In Spring 3 the - AnnotationMethodHandlerAdapter has been - extended to support the @RequestBody and has - several HttpMessageConverters - registered by default, these are - - - - ByteArrayHttpMessageConverter - - converts byte arrays - - - - StringHttpMessageConverter - converts - strings - - - - FormHttpMessageConverter - converts - form data to/from a MultiValueMap<String, String> - - - - SourceHttpMessageConverter - converts - to/from a javax.xml.transform.Source; - - - - MarshallingHttpMessageConverter - - converts to/from an object using the - org.springframework.oxm package. - - - - More information on these converters can be found in the section - Message - Converters. - - The MarshallingHttpMessageConverter - requires a Marshaller and - Unmarshaller from the - org.springframework.oxm package to be - configured on an instance of - AnnotationMethodHandlerAdapter in the - application context. For example - - <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> - <property name="messageConverters"> - <util:list id="beanList"> - <ref bean="stringHttpMessageConverter"/> - <ref bean="marshallingHttpMessageConverter"/> - </util:list> - </property -</bean> - -<bean id="stringHttpMessageConverter" - class="org.springframework.http.converter.StringHttpMessageConverter"/> - -<bean id="marshallingHttpMessageConverter" - class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter"> - <property name="marshaller" ref="castorMarshaller" /> - <property name="unmarshaller" ref="castorMarshaller" /> -</bean> - -<bean id="castorMarshaller" class="org.springframework.oxm.castor.CastorMarshaller"/> - -
-
- -
- Returning multiple representations - - A RESTful architecture may expose multiple representations of a - resource. There are two strategies for a client to inform the server of - the representation it is interested in receiving. - - The first strategy is to use a distinct URI for each resource. - This is typically done by using a different file extension in the URI. - For example the URI - http://www.example.com/users/fred.pdf requests a PDF - representation of the user fred while - http://www.example.com/users/fred.xml requests an XML - representation. - - The second strategy is for the client to use the same URI to - locate the resource but set the Accept HTTP request - header to list the media - types that it understands. For example, a HTTP request for - http://www.example.com/users/fred with an - Accept header set to application/pdf - requests a PDF representation of the user fred while - http://www.example.com/users/fred with an - Accept header set to text/xml - requests an XML representation. This strategy is known as content - negotiation. - - - One issue with the Accept header is that is impossible to change - it in a web browser, in HTML. For instance, in Firefox, it's fixed - to - - Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 - - - For this reason it is common to see the use of a distinct URI - for each representation. - - - To support multiple representations of a resource Spring provides - the ContentNegotiatingViewResolver to resolve a - view based on the file extension or Accept header of - the HTTP request. ContentNegotiatingViewResolver - does not perform the view resolution itself, but instead delegates to a - list of view resolvers set using the bean property - ViewResolvers. - - The ContentNegotiatingViewResolver selects - an appropriate View to handle the request by - comparing the request media type(s) with the media type (a.k.a. - Content-Type) supported by the - View associated with each of its - ViewResolvers. The first - View in the list that has a compatible - Content-Type is used to return the representation to - the client. The Accept header may include wild cards, - for example 'text/*', in which case a View whose - Context-Type was 'text/xml' is a compatible match. - - To support the resolution of a view based on a file extension, - ContentNegotiatingViewResolver's bean property - MediaTypes is used to specify a mapping of file - extensions to media types. For more information on the algorithm to - determine the request media type, refer to the API documentation for - ContentNegotiatingViewResolver.. - - Here is an example configuration of a - ContentNegotiatingViewResolver - - <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> - <property name="mediaTypes"> - <map> - <entry key="atom" value="application/atom+xml"/> - <entry key="html" value="text/html"/> - </map> - </property> - <property name="viewResolvers"> - <list> - <bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> - <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> - <property name="prefix" value="/WEB-INF/jsp/"/> - <property name="suffix" value=".jsp"/> - </bean> - </list> - </property> - </bean> - - - <bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/> - - The InternalResourceViewResolver handles - the translation of view names and JSP pages while the - BeanNameViewResolver returns a view based on the - name of a bean. (See "Resolving views - the ViewResolver - interface" for more details on how Spring looks up and - instantiates a view.) In this example, the content - bean is a class that inherits from - AbstractAtomFeedView which returns an Atom RSS - feed. For more information on creating an Atom Feed representation see - the section 'Atom Views'. - - In this configuration, if a request is made with a .html extension - the view resolver will look for a view that matches the text/html media - type. The InternalResourceViewResolver provides - the matching view for text/html. If the request is made with the file - extension .atom, the view resolver will look for a view that matches the - application/atom+xml media type. This view is provided by the - BeanNameViewResolver that maps to the - SampleContentAtomView if the view name returned - is 'content'. Alternatively, client requests could be made without a - file extension and setting the Accept header to the preferred media-type - and the same resolution of request to views would occur. - - - If ContentNegotiatingViewResolver's list - of ViewResolvers is not configured explicitly, then it will - automatically use any ViewResolvers defined in the application - context. - - - The corresponding controller code that returns an Atom RSS feed - for a URI of the form http://localhost/content.atom - or http://localhost/content with an - Accept header of application/atom+xml is shown - below - - @Controller -public class ContentController { - - private List<SampleContent> contentList = new ArrayList<SampleContent>(); - - @RequestMapping(value="/content", method=RequestMethod.GET) - public ModelAndView getContent() { - ModelAndView mav = new ModelAndView(); - mav.setViewName("content"); - mav.addObject("sampleContentList", contentList); - return mav; - } - -} - - -
Views @@ -638,333 +276,5 @@ public class SimpleController { ModelAndView object. Please refer to the API documentation for more details.
-
-
- Accessing RESTful services on the Client - - The RestTemplate is the core class for - client-side access to RESTful services. It is conceptually similar to - other template classes in Spring, such as - JdbcTemplate and JmsTemplate - and other template classes found in other Spring portfolio projects. - RestTemplate's behavior is customized by providing - callback methods and configuring the - HttpMessageConverter used to marshal - objects into the HTTP request body and to unmarshall any response back - into an object. As it is common to use XML as a message format, Spring - provides a MarshallingHttpMessageConverter that - uses the Object-to-XML framework that is part of the - org.springframework.oxm package. This gives you a - wide range of choices of XML to Object mapping technologies to choose - from. - - This section describes how to use the - RestTemplate and its associated - HttpMessageConverters. - -
- RestTemplate - - Invoking RESTful services in Java is typically done using a helper - class such as Jakarta Commons HttpClient. For - common REST operations this approach is too low level as shown - below. - - String uri = "http://example.com/hotels/1/bookings"; - -PostMethod post = new PostMethod(uri); -String request = // create booking request content -post.setRequestEntity(new StringRequestEntity(request)); - -httpClient.executeMethod(post); - -if (HttpStatus.SC_CREATED == post.getStatusCode()) { - Header location = post.getRequestHeader("Location"); - if (location != null) { - System.out.println("Created new booking at :" + location.getValue()); - } -} - - RestTemplate provides higher level methods that correspond to each - of the six main HTTP methods that make invoking many RESTful services a - one-liner and enforce REST best practices. - - - Overview of RestTemplate methods - - - - - HTTP Method - - RestTemplate - Method - - - - DELETE - - delete(String - url, String… urlVariables) - - - - GET - - getForObject(String - url, Class<T> responseType, String… - urlVariables) - - - - HEAD - - headForHeaders(String - url, String… urlVariables) - - - - OPTIONS - - optionsForAllow(String - url, String… urlVariables) - - - - POST - - postForLocation(String - url, Object request, String… urlVariables) - - - - PUT - - put(String - url, Object request, String…urlVariables) - - - -
- - The names of RestTemplate methods follow a - naming convention, the first part indicates what HTTP method is being - invoked and the second part indicates what is returned. For example, the - method getForObject will perform a GET, convert - the HTTP response into an object type of your choice and return that - object. The method postForLocation will do a - POST, converting the given object into a HTTP request and return the - response HTTP Location header where the newly created object can be - found In case of an exception processing the HTTP request, an exception - of the type RestClientException will be - thrown. - - Objects passed to and returned from these methods are converted to - and from HTTP messages by - HttpMessageConverter instances. - Converters for the main mime types are registered by default, but you - can also write your own converter and register it via the - messageConverters bean property. The default - converter instances registered with the template are - ByteArrayHttpMessageConverter, - StringHttpMessageConverter, - FormHttpMessageConverter and - SourceHttpMessageConverter. You can override - these defaults using the messageConverters bean - property as would be required if using the - MarshallingHttpMessageConverter. - - Each method takes URI template arguments in two forms, either as a - String variable length argument or a - Map<String,String>. For example, - - String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", - String.class,"42", "21"); - - - using variable length arguments and - - Map<String, String> vars = Collections.singletonMap("hotel", "42"); -String result = - restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars); - - - using a Map<String,String>. - - To create an instance of RestTemplate you - can simply call the default constructor. This will use standard Java - classes from the java.net package as the underlying - implementation to create HTTP requests. This can be overridden by - specifying an implementation of - ClientHttpRequestFactory. Spring provides - the implementation - CommonsClientHttpRequestFactory that uses the - Jakarta Commons HttpClient to create requests. - CommonsClientHttpRequestFactory is configured - using an instance of - org.apache.commons.httpclient.HttpClient which - can in turn be configured with credentials information or connection - pooling functionality. - - The previous example using Jakarta Commons - HttpClient directly rewritten to use the - RestTemplate is shown below - - uri = "http://example.com/hotels/{id}/bookings"; - -RestTemplate template = new RestTemplate(); - -Booking booking = // create booking object - -URI location = template.postForLocation(uri, booking, "1"); - - - The general callback interface is - RequestCallback and is called when the - execute method is invoked. - - public <T> T execute(String url, HttpMethod method, RequestCallback requestCallback, - ResponseExtractor<T> responseExtractor, - String... urlVariables) - - -// also has an overload with urlVariables as a Map<String, String>. - - The RequestCallback interface is - defined as - - public interface RequestCallback { - void doWithRequest(ClientHttpRequest request) throws IOException; -} - - and allows you to manipulate the request headers and write to the - request body. When using the execute method you do not have to worry - about any resource management, the template will always close the - request and handle any errors. Refer to the API documentation for more - information on using the execute method and the meaning of its other - method arguments. -
- -
- HTTP Message Conversion - - Objects passed to and returned from the methods - getForObject, - postForLocation, and - put are converted to HTTP requests and from - HTTP responses by HttpMessageConverters. - The HttpMessageConverter interface is - shown below to give you a better feel for its functionality - - public interface HttpMessageConverter<T> { - - // Indicate whether the given class is supported by this converter. - boolean supports(Class<? extends T> clazz); - - // Return the list of MediaType objects supported by this converter. - List<MediaType> getSupportedMediaTypes(); - - // Read an object of the given type form the given input message, and returns it. - T read(Class<T> clazz, HttpInputMessage inputMessage) throws IOException, - HttpMessageNotReadableException; - - // Write an given object to the given output message. - void write(T t, HttpOutputMessage outputMessage) throws IOException, - HttpMessageNotWritableException; - -} - - Concrete implementations for the main media (mime) types are - provided in the framework and are registered by default with the - RestTemplate on the client-side and with - AnnotationMethodHandlerAdapter on the - server-side. - - The implementations of - HttpMessageConverters are described in the - following sections. For all converters a default media type is used but - can be overridden by setting the - supportedMediaTypes bean property - -
- StringHttpMessageConverter - - An HttpMessageConverter - implementation that can read and write Strings from the HTTP request - and response. By default, this converter supports all text media types - (text/*), and writes with a - Content-Type of - text/plain. -
- -
- FormHttpMessageConverter - - An HttpMessageConverter - implementation that can read and write form data from the HTTP request - and response. By default, this converter reads and writes the media - type application/x-www-form-urlencoded. Form data - is read from and written into a MultiValueMap<String, - String>. -
- -
- ByteArrayMessageConverter - - An HttpMessageConverter - implementation that can read and write byte arrays from the HTTP - request and response. By default, this converter supports all media - types (*/*), and writes with a - Content-Type of - application/octet-stream. This can be overridden by - setting the supportedMediaTypes property, and - overriding getContentType(byte[]). -
- -
- MarshallingHttpMessageConverter - - An HttpMessageConverter - implementation that can read and write XML using Spring's - Marshaller and - Unmarshaller abstractions from the - org.springframework.oxm package. This converter - requires a Marshaller and - Unmarshaller before it can be used. - These can be injected via constructor or bean properties. By default - this converter supports (text/xml) and - (application/xml). -
- -
- SourceHttpMessageConverter - - An HttpMessageConverter - implementation that can read and write - javax.xml.transform.Source from the HTTP - request and response. Only DOMSource, - SAXSource, and - StreamSource are supported. By default, this - converter supports (text/xml) and - (application/xml). -
- -
- BufferedImageHttpMessageConverter - - An HttpMessageConverter - implementation that can read and write - java.awt.image.BufferedImage from the HTTP - request and response. This converter reads and writes the media type - supported by the Java I/O API. -
-
-