diff --git a/spring-framework-reference/src/expressions.xml b/spring-framework-reference/src/expressions.xml
index 2c17838c64d..b94c2d11f39 100644
--- a/spring-framework-reference/src/expressions.xml
+++ b/spring-framework-reference/src/expressions.xml
@@ -425,7 +425,7 @@ Object nullValue = parser.parseExpression("null").getValue();
Navigating with property references is easy, just use a period to
indicate a nested property value. The instances of Inventor class, pupin
and tesla, were populated with data listed in section Section Classes used in the
+ linkend="expressions-example-classes">Classes used in the
examples. To navigate "down" and get Tesla's year of birth and
Pupin's city of birth the following expressions are used
diff --git a/spring-framework-reference/src/rest.xml b/spring-framework-reference/src/rest.xml
index f45b9a8984f..6b0debad794 100644
--- a/spring-framework-reference/src/rest.xml
+++ b/spring-framework-reference/src/rest.xml
@@ -1,8 +1,6 @@
-
-
- Spring REST support
+
+ REST supportIntroduction
@@ -23,7 +21,7 @@
url="http://blog.springsource.com/2009/03/08/rest-in-spring-3-mvc/">entry.)
With little effort, you can marshall data out of a RESTful request using
@RequestMapping and @PathVariable annotations and return different views
- as determined by the request's Context-Type header.
+ as determined by the request's Context-Type header.
In this chapter we describe all the features of Spring's REST
support. It is divided into two main two chapters, one for the server-side
@@ -31,7 +29,7 @@
linkend="mvc">MVC framework, you may want to read through the
reference documentation on annotation-based
controller configuration to undestand the general programming
- model.
+ model.
@@ -40,7 +38,7 @@
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.
+ linkend="mvc-servlet">DispatcherServlet.
URI templates
@@ -60,7 +58,7 @@
http://www.example.com/users/fredWhen processing an request the URI can be compared to an expected
- URI Template in order to extract a collection of variables.
+ URI Template in order to extract a collection of variables.
Spring uses the @RequestMapping annotation
to define the URI Template for the request.
@@ -75,15 +73,14 @@ public String getUser(@PathVariable String userId) {
}
The request http://www.example.com/users/fred
- will bind the userId method parameter to the String value 'fred'.
-
+ will bind the userId method parameter to the String value 'fred'.Mapping RESTful URLs with the @PathVariable annotationThe @PathVariable method level annotation
is used to indicate that a method parameter should be bound to the
- value of a URI template variable.
+ value of a URI template variable.
The following code snippet shows the use of a single
@PathVariable in a controller method:
@@ -101,7 +98,7 @@ public String findOwner(@PathVariable String ow
request, the value of ownerId is set the 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.
+ ownerId.
The matching of method parameter names to URI Template variable
names can only be done if your code is compiled with debugging
@@ -109,7 +106,7 @@ public String findOwner(@PathVariable String ow
name of the URI Template variable name to bind to in the @PathVariable
annotation. For example
- @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
+ @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String ownerId, Model model) {
// implementation omitted
}
@@ -119,7 +116,7 @@ public String findOwner(@PathVariable("ownerId"
so you may also use create a controlled method with the signature
shown below
- @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
+ @RequestMapping("/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(@PathVariable("ownerId") String theOwner, Model model) {
// implementation omitted
}
@@ -163,9 +160,150 @@ public class RelativePathUriTemplateController {
- Content Negotiation
+ Returning multiple representations
- blah blah
+ 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 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 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 be 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;
+ }
+
+}
+
+
@@ -177,13 +315,86 @@ public class RelativePathUriTemplateController {
HTTP Method Conversion
- blah blah
+ Another key principle of REST is the use of the Uniform Interface.
+ This means that all resources (URLs) can be manipulated using the same
+ four HTTP method: GET, PUT, POST, and DELETE. For each of methods, the
+ HTTP specification defines exact semantics. For instance, a GET should
+ always be a safe operation, meaning that is has no side effects, and a
+ PUT or DELETE should be idempotent, meaning that you can repeat these
+ operations over and over again, but the end result should be the same.
+ While HTTP defines these four methods, HTML only supports two: GET and
+ POST. Fortunately, there are two possible workarounds: you can either
+ use JavaScript to do your PUT or DELETE, or simply do a POST with the
+ 'real' method as an additional parameter (modeled as a hidden input
+ field in an HTML form). This latter trick is what the
+ HiddenHttpMethodFilter does. This filter was
+ introduced in Spring 3.0 M1, and is a plain Servlet Filter. As such, it
+ can be used in combination with any web framework (not just Spring MVC).
+ Simply add this filter to your web.xml, and a POST with a hidden _method
+ parameter will be converted into the corresponding HTTP method
+ request.
+
+
+ Supporting Spring form tags
+
+ To support HTTP method conversion the Spring MVC form tag was
+ updated to support setting the HTTP method. For example, the following
+ snippet taken from the updated Petclinic sample
+
+ <form:form method="delete">
+ <p class="submit"><input type="submit" value="Delete Pet"/></p>
+</form:form>
+
+ This will actually perform an HTTP POST, with the 'real' DELETE
+ method hidden behind a request parameter, to be picked up by the
+ HiddenHttpMethodFilter. The corresponding
+ @Controller method is shown below
+
+ @RequestMapping(method = RequestMethod.DELETE)
+public String deletePet(@PathVariable int ownerId, @PathVariable int petId) {
+ this.clinic.deletePet(petId);
+ return "redirect:/owners/" + ownerId;
+}
+ ETag support
- blah blah
+ An ETag (entity tag)
+ is an HTTP response header returned by an HTTP/1.1 compliant web server
+ used to determine change in content at a given URL. It can be considered
+ to be the more sophisticated successor to the
+ Last-Modified header. When a server returns a
+ representation with an ETag header, client can use this header in
+ subsequent GETs, in a If-None-Match header. If the
+ content has not changed, the server will return 304: Not
+ Modified.
+
+ Support for ETags is provided by the servlet filter
+ ShallowEtagHeaderFilter. Since it is a plain
+ Servlet Filter, and thus can be used in combination any web framework.
+ The ShallowEtagHeaderFilter filter creates
+ so-called shallow ETags (as opposed to a deep ETags, more about that
+ later). The way it works is quite simple: the filter simply caches the
+ content of the rendered JSP (or other content), generates a MD5 hash
+ over that, and returns that as a ETag header in the response. The next
+ time a client sends a request for the same resource, it use that hash as
+ the If-None-Match value. The filter notices this,
+ renders the view again, and compares the two hashes. If they are equal,
+ a 304 is returned. It is important to note that this
+ filter will not save processing power, as the view is still rendered.
+ The only thing it saves is bandwith, as the rendered response is not
+ sent back over the wire.
+
+ Deep ETags are a bit more complicated. In this case, the ETag is
+ based on the underlying domain objects, RDMBS tables, etc. Using this
+ approach, no content is generated unless the underlying data has
+ changed. Unfortunately, implementing this approach in a generic way is
+ much more difficult than shallow ETags. Spring may provide support for
+ deep ETags in a later release by relying on JPA's @Version annotation,
+ or an AspectJ aspect.