Polishing the MVC sections of the reference manual.

This commit is contained in:
Sam Brannen 2009-10-19 22:32:15 +00:00
parent 32fb767c87
commit ccb103417d
1 changed files with 184 additions and 161 deletions

View File

@ -1399,8 +1399,8 @@ public class EditPetForm {
<programlisting>JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84</programlisting> <programlisting>JSESSIONID=415A4AC178C59DACE0B2C9CA727CDD84</programlisting>
<para>The following code sample allows you to easily get the value of <para>The following code sample demonstrates how to get the value of
the "JSESSIONID"cookie:</para> the <literal>JSESSIONID</literal> cookie:</para>
<programlisting language="java">@RequestMapping("/displayHeaderInfo.do") <programlisting language="java">@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(<emphasis role="bold">@CookieValue("JSESSIONID")</emphasis> String cookie) { public void displayHeaderInfo(<emphasis role="bold">@CookieValue("JSESSIONID")</emphasis> String cookie) {
@ -1420,7 +1420,7 @@ public void displayHeaderInfo(<emphasis role="bold">@CookieValue("JSESSIONID")</
<para>The <interfacename>@RequestHeader</interfacename> annotation <para>The <interfacename>@RequestHeader</interfacename> annotation
allows a method parameter to be bound to a request header.</para> allows a method parameter to be bound to a request header.</para>
<para>Here is a request header sample:</para> <para>Here is a sample request header:</para>
<programlisting> <programlisting>
Host localhost:8080 Host localhost:8080
@ -1431,12 +1431,12 @@ Accept-Charset ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive 300 Keep-Alive 300
</programlisting> </programlisting>
<para>The following code sample allows you to easily get the value of <para>The following code sample demonstrates how to get the value of
the "Accept-Encoding" and "Keep-Alive" headers:</para> the <literal>Accept-Encoding</literal> and <literal>Keep-Alive</literal> headers:</para>
<programlisting language="java">@RequestMapping("/displayHeaderInfo.do") <programlisting language="java">@RequestMapping("/displayHeaderInfo.do")
public void displayHeaderInfo(<emphasis role="bold">@RequestHeader("Accept-Encoding")</emphasis> String encoding, public void displayHeaderInfo(<emphasis role="bold">@RequestHeader("Accept-Encoding")</emphasis> String encoding,
<emphasis role="bold">@RequestHeader("Keep-Alive")</emphasis> long keepAlive) { <emphasis role="bold">@RequestHeader("Keep-Alive")</emphasis> long keepAlive) {
//... //...
@ -1464,7 +1464,7 @@ Keep-Alive 300
<interfacename>@InitBinder</interfacename> allows you to configure <interfacename>@InitBinder</interfacename> allows you to configure
web data binding directly within your controller class. web data binding directly within your controller class.
<interfacename>@InitBinder</interfacename> identifies methods that <interfacename>@InitBinder</interfacename> identifies methods that
initialize the <classname>WebDataBinder</classname>, which will be initialize the <classname>WebDataBinder</classname> that will be
used to populate command and form object arguments of annotated used to populate command and form object arguments of annotated
handler methods.</para> handler methods.</para>
@ -1538,66 +1538,88 @@ public class MyFormController {
<classname>DefaultAnnotationHandlerMapping</classname>, which looks for <classname>DefaultAnnotationHandlerMapping</classname>, which looks for
<interfacename>@RequestMapping</interfacename> annotations on <interfacename>@RequestMapping</interfacename> annotations on
<interfacename>@Controllers</interfacename>. Typically, you do not need to <interfacename>@Controllers</interfacename>. Typically, you do not need to
override this default mapping, except when overriding the properties. override this default mapping, unless you need to override the default property values.
These properties are:</para> These properties are:</para>
<itemizedlist spacing="compact"> <variablelist>
<listitem> <varlistentry>
<para><literal>interceptors</literal>: List of interceptors to use. <term><literal>interceptors</literal></term>
<interfacename>HandlerInterceptor</interfacename>s are discussed in <listitem>
<xref linkend="mvc-handlermapping-interceptor" />.</para> <para>
</listitem> List of interceptors to use.
<interfacename>HandlerInterceptor</interfacename>s are discussed in
<xref linkend="mvc-handlermapping-interceptor" />.
</para>
</listitem>
</varlistentry>
<listitem> <varlistentry>
<para><literal>defaultHandler</literal>: Default handler to use, when <term><literal>defaultHandler</literal></term>
this handler mapping does not result in a matching handler.</para> <listitem>
</listitem> <para>Default handler to use, when this handler mapping does not result
in a matching handler.</para>
</listitem>
</varlistentry>
<listitem> <varlistentry>
<para><literal>order</literal>: Based on the value of the order <term><literal>order</literal></term>
property (see the <literal>org.springframework.core.Ordered</literal> <listitem>
interface), Spring sorts all handler mappings available in the context <para>
and applies the first matching handler.</para> Based on the value of the order property (see the
</listitem> <literal>org.springframework.core.Ordered</literal>
interface), Spring sorts all handler mappings available in the context
and applies the first matching handler.
</para>
</listitem>
</varlistentry>
<listitem> <varlistentry>
<para><literal>alwaysUseFullPath</literal>: If <term><literal>alwaysUseFullPath</literal></term>
<literal>true</literal>, Spring uses the full path within the current <listitem>
servlet context to find an appropriate handler. If <para>
<literal>false</literal> (the default), the path within the current If <literal>true</literal> , Spring uses the full path within the current
servlet mapping is used. For example, if a servlet is mapped using servlet context to find an appropriate handler. If <literal>false</literal>
<literal>/testing/*</literal> and the (the default), the path within the current servlet mapping is used. For
<literal>alwaysUseFullPath</literal> property is set to true, example, if a servlet is mapped using <literal>/testing/*</literal>
<literal>/testing/viewPage.html</literal> is used, whereas if the and the <literal>alwaysUseFullPath</literal> property is set to true,
property is set to false, <literal>/viewPage.html</literal> is <literal>/testing/viewPage.html</literal> is used, whereas if the
used.</para> property is set to false, <literal>/viewPage.html</literal> is used.
</listitem> </para>
</listitem>
</varlistentry>
<listitem> <varlistentry>
<para><literal>urlDecode</literal>: Defaults to <term><literal>urlDecode</literal></term>
<literal>true</literal>, as of Spring 2.5. <!--OK, or do you mean 3.0?-->If <listitem>
you prefer to compare encoded paths, switch this flag to <para>
<literal>false</literal>. However, the Defaults to <literal>true</literal>, as of Spring 2.5. <!--OK, or do you mean 3.0?-->
<interfacename>HttpServletRequest</interfacename> always exposes the If you prefer to compare encoded paths, set this flag to <literal>false</literal>.
servlet path in decoded form. Be aware that the servlet path will not However, the <interfacename>HttpServletRequest</interfacename> always exposes the
match when compared with encoded paths.</para> servlet path in decoded form. Be aware that the servlet path will not match when
</listitem> compared with encoded paths.
</para>
</listitem>
</varlistentry>
<listitem> <varlistentry>
<para><literal>lazyInitHandlers</literal>: Allows lazy initialization <term><literal>lazyInitHandlers</literal></term>
of <emphasis>singleton</emphasis> handlers (prototype handlers are <listitem>
always lazy-initialized). The default value is <para>
<literal>false</literal>.</para> Allows lazy initialization of <emphasis>singleton</emphasis>
</listitem> handlers (prototype handlers are always lazy-initialized).
</itemizedlist> The default value is <literal>false</literal>.
</para>
</listitem>
</varlistentry>
</variablelist>
<para><note> <note>
<para>The <para>
<literal>alwaysUseFullPath<literal>,</literal>urlDecode</literal>, and The <literal>alwaysUseFullPath</literal>, <literal>urlDecode</literal>, and
<literal>lazyInitHandlers</literal> properties are only available to <literal>lazyInitHandlers</literal> properties are only available to subclasses of
subclasses of <interfacename>org.springframework.web.servlet.handler.AbstractUrlHandlerMapping</interfacename>.
<interfacename>org.springframework.web.servlet.handler.AbstractUrlHandlerMapping</interfacename>.</para> </para>
</note></para> </note>
<para>The following example shows how to override the default mapping and <para>The following example shows how to override the default mapping and
add an interceptor:</para> add an interceptor:</para>
@ -1633,7 +1655,7 @@ public class MyFormController {
<para>The <literal>preHandle(..)</literal> method returns a boolean <para>The <literal>preHandle(..)</literal> method returns a boolean
value. You can use this method to break or continue the processing of value. You can use this method to break or continue the processing of
the execution chain. When this method returns <literal>true</literal>, the execution chain. When this method returns <literal>true</literal>,
the handler execution chain will continue, when it returns false, the the handler execution chain will continue; when it returns false, the
<classname>DispatcherServlet</classname> assumes the interceptor itself <classname>DispatcherServlet</classname> assumes the interceptor itself
has taken care of requests (and, for example, rendered an appropriate has taken care of requests (and, for example, rendered an appropriate
view) and does not continue executing the other interceptors and the view) and does not continue executing the other interceptors and the
@ -1732,10 +1754,12 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<title>Resolving views with the <title>Resolving views with the
<interfacename>ViewResolver</interfacename> interface</title> <interfacename>ViewResolver</interfacename> interface</title>
<para>As discussed in <xref linkend="mvc-controller" />, all controllers <para>As discussed in <xref linkend="mvc-controller" />, all handler
in the Spring Web MVC framework return a methods in the Spring Web MVC controllers must resolve to a logical
<classname>ModelAndView</classname> instance. Views in Spring are view name, either explicitly (e.g., by returning a <literal>String</literal>,
addressed by a view name and are resolved by a view resolver. Spring <literal>View</literal>, or <literal>ModelAndView</literal>) or implicitly
(i.e., based on conventions). Views in Spring are
addressed by a logical view name and are resolved by a view resolver. Spring
comes with quite a few view resolvers. This table lists most of them; a comes with quite a few view resolvers. This table lists most of them; a
couple of examples follow.</para> couple of examples follow.</para>
@ -1791,9 +1815,9 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<entry>Simple implementation of the <entry>Simple implementation of the
<interfacename>ViewResolver</interfacename> interface that <interfacename>ViewResolver</interfacename> interface that
effects the direct resolution of symbolic view names to URLs, effects the direct resolution of logical view names to URLs,
without an explicit mapping definition. This is appropriate if without an explicit mapping definition. This is appropriate if
your symbolic names match the names of your view resources in a your logical names match the names of your view resources in a
straightforward manner, without the need for arbitrary straightforward manner, without the need for arbitrary
mappings.</entry> mappings.</entry>
</row> </row>
@ -1801,10 +1825,10 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<row> <row>
<entry><classname>InternalResourceViewResolver</classname></entry> <entry><classname>InternalResourceViewResolver</classname></entry>
<entry>Convenience subclass of <entry>Convenient subclass of
<classname>UrlBasedViewResolver</classname> that supports <classname>UrlBasedViewResolver</classname> that supports
<classname>InternalResourceView</classname> (in effect, Servlets <classname>InternalResourceView</classname> (in effect, Servlets
and JSPs), and subclasses such as and JSPs) and subclasses such as
<classname>JstlView</classname> and <classname>JstlView</classname> and
<classname>TilesView</classname>. You can specify the view class <classname>TilesView</classname>. You can specify the view class
for all views generated by this resolver by using for all views generated by this resolver by using
@ -1817,7 +1841,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<entry><classname>VelocityViewResolver</classname> / <entry><classname>VelocityViewResolver</classname> /
<classname>FreeMarkerViewResolver</classname></entry> <classname>FreeMarkerViewResolver</classname></entry>
<entry>Convenience subclass of <entry>Convenient subclass of
<classname>UrlBasedViewResolver</classname> that supports <classname>UrlBasedViewResolver</classname> that supports
<classname>VelocityView</classname> (in effect, Velocity <classname>VelocityView</classname> (in effect, Velocity
templates) or <classname>FreeMarkerView</classname> templates) or <classname>FreeMarkerView</classname>
@ -1849,7 +1873,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
&lt;property name="suffix" value=".jsp"/&gt; &lt;property name="suffix" value=".jsp"/&gt;
&lt;/bean&gt;</programlisting> &lt;/bean&gt;</programlisting>
<para>When returning <literal>test</literal> as a viewname, this view <para>When returning <literal>test</literal> as a logical view name, this view
resolver forwards the request to the resolver forwards the request to the
<classname>RequestDispatcher</classname> that will send the request to <classname>RequestDispatcher</classname> that will send the request to
<literal>/WEB-INF/jsp/test.jsp</literal>.</para> <literal>/WEB-INF/jsp/test.jsp</literal>.</para>
@ -1876,7 +1900,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<note> <note>
<para>Subclasses of <classname>AbstractCachingViewResolver</classname> <para>Subclasses of <classname>AbstractCachingViewResolver</classname>
cache view instances that they resolve. Caching improves performance cache view instances that they resolve. Caching improves performance
of certain view technologies. It's possible to turn off the cache, by of certain view technologies. It's possible to turn off the cache by
setting the <literal>cache</literal> property to setting the <literal>cache</literal> property to
<literal>false</literal>. Furthermore, if you must refresh a certain <literal>false</literal>. Furthermore, if you must refresh a certain
view at runtime (for example when a Velocity template is modified), view at runtime (for example when a Velocity template is modified),
@ -1892,12 +1916,12 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
resolvers and, for example, override specific views in certain resolvers and, for example, override specific views in certain
circumstances. You chain view resolvers by adding more than one resolver circumstances. You chain view resolvers by adding more than one resolver
to your application context and, if necessary, by setting the to your application context and, if necessary, by setting the
<literal>order</literal> property to specify an order. Remember, the <literal>order</literal> property to specify ordering. Remember, the
higher the order property, the later the view resolver is positioned in higher the order property, the later the view resolver is positioned in
the chain.</para> the chain.</para>
<para>In the following example, the chain of view resolvers consists of <para>In the following example, the chain of view resolvers consists of
two resolvers, a <classname>InternalResourceViewResolver</classname>, two resolvers, an <classname>InternalResourceViewResolver</classname>,
which is always automatically positioned as the last resolver in the which is always automatically positioned as the last resolver in the
chain, and an <classname>XmlViewResolver</classname> for specifying chain, and an <classname>XmlViewResolver</classname> for specifying
Excel views. Excel views are not supported by the Excel views. Excel views are not supported by the
@ -1923,7 +1947,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<para>If a specific view resolver does not result in a view, Spring <para>If a specific view resolver does not result in a view, Spring
examines the context for other view resolvers. If additional view examines the context for other view resolvers. If additional view
resolvers exist, Spring continues to inspect them. <!--So what happens after Spring inspects them?-->If resolvers exist, Spring continues to inspect them. <!--So what happens after Spring inspects them?-->If
they do not, it throws an <classname>Exception</classname>.</para> they do not exist, Spring throws an <classname>Exception</classname>.</para>
<para>The contract of a view resolver specifies that a view resolver <para>The contract of a view resolver specifies that a view resolver
<emphasis>can</emphasis> return null to indicate the view could not be <emphasis>can</emphasis> return null to indicate the view could not be
@ -1950,30 +1974,32 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
view name, which a view resolver resolves to a particular view view name, which a view resolver resolves to a particular view
technology. For view technologies such as JSPs that are processed technology. For view technologies such as JSPs that are processed
through the Servlet or JSP engine, this resolution is usually handled through the Servlet or JSP engine, this resolution is usually handled
through <classname>InternalResourceViewResolver</classname> / through the combination of <classname>InternalResourceViewResolver</classname> and
<classname>InternalResourceView</classname>, which <classname>InternalResourceView</classname>, which
issues an internal forward or include, through the Servlet API's issues an internal forward or include via the Servlet API's
<literal>RequestDispatcher.forward(..)</literal> or <literal>RequestDispatcher.forward(..)</literal> method or
<literal>RequestDispatcher.include()</literal>. For other view <literal>RequestDispatcher.include()</literal> method. For other view
technologies, such as Velocity, XSLT, and so on, the view itself technologies, such as Velocity, XSLT, and so on, the view itself
produces the content on the response stream.</para> writes the content directly to the response stream.</para>
<para>It is sometimes desirable to issue an HTTP redirect back to the <para>It is sometimes desirable to issue an HTTP redirect back to the
client, before the view is rendered. This is desirable for example when client, before the view is rendered. This is desirable, for example, when
one controller has been called with <literal>POST</literal>ed data, and one controller has been called with <literal>POST</literal>ed data, and
the response is actually a delegation to another controller (for example the response is actually a delegation to another controller (for example
on a successful form submission). In this case, a normal internal on a successful form submission). In this case, a normal internal
forward will mean the other controller will also see the same forward will mean that the other controller will also see the same
<literal>POST</literal> data, which is potentially problematic if it can <literal>POST</literal> data, which is potentially problematic if it can
confuse it with other expected data. Another reason to do a redirect confuse it with other expected data. Another reason to perform a redirect
before displaying the result is that this will eliminate the possibility before displaying the result is to eliminate the possibility
of the user doing a double submission of form data. The browser will of the user submitting the form data multiple times. In this scenario,
have sent the initial <literal>POST</literal>, will have seen a redirect the browser will first send an initial <literal>POST</literal>; it will
back and done a subsequent <literal>GET</literal> because of that, and then receive a response to redirect to a different URL; and finally
thus as far as it is concerned, the current page does not reflect the the browser will perform a subsequent <literal>GET</literal> for the
result of a <literal>POST</literal>, but rather of a URL named in the redirect response. Thus, from the perspective of the
<literal>GET</literal>, so there is no way the user can accidentally browser, the current page does not reflect the result of a
re-<literal>POST</literal> the same data by doing a refresh. The refresh <literal>POST</literal> but rather of a <literal>GET</literal>. The
end effect is that there is no way the user can accidentally
re-<literal>POST</literal> the same data by performing a refresh. The refresh
forces a <literal>GET</literal> of the result page, not a resend of the forces a <literal>GET</literal> of the result page, not a resend of the
initial <literal>POST</literal> data.</para> initial <literal>POST</literal> data.</para>
@ -1989,12 +2015,12 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
instructs the view to do its work.</para> instructs the view to do its work.</para>
<para>The <classname>RedirectView</classname> issues an <para>The <classname>RedirectView</classname> issues an
<literal>HttpServletResponse.sendRedirect()</literal> call, which <literal>HttpServletResponse.sendRedirect()</literal> call that
comes back to the client browser as an HTTP redirect. <!--Does preceding happen after what happens in first paragraph? Clarify sequence of events.-->All returns to the client browser as an HTTP redirect. <!--Does preceding happen after what happens in first paragraph? Clarify sequence of events.-->All
model attributes are exposed as HTTP query parameters. This does mean model attributes are exposed as HTTP query parameters. This means
that the model must contain only objects (generally Strings or that the model must contain only objects (generally Strings or
convertible to Strings), which can be readily converted to a objects converted to a String representation), which can be readily converted to a
string-form HTTP query parameter.</para> textual HTTP query parameter.</para>
<para>If you use <classname>RedirectView</classname> and the view is <para>If you use <classname>RedirectView</classname> and the view is
created by the controller itself, it is recommended that you configure created by the controller itself, it is recommended that you configure
@ -2008,18 +2034,18 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<title>The <literal>redirect:</literal> prefix</title> <title>The <literal>redirect:</literal> prefix</title>
<para>While the use of <classname>RedirectView</classname> works fine, <para>While the use of <classname>RedirectView</classname> works fine,
if the controller itself is creating the if the controller itself creates the
<classname>RedirectView</classname>, there is no getting around the <classname>RedirectView</classname>, there is no avoiding the
fact that the controller is aware that a redirection is happening. fact that the controller is aware that a redirection is happening.
This is really suboptimal and couples things too tightly. The This is really suboptimal and couples things too tightly. The
controller should not really care about how the response gets controller should not really care about how the response gets
handled. In general it should operate only in terms of view names that handled. In general it should operate only in terms of view names that
have been injected into it.</para> have been injected into it.</para>
<para>The special <literal>redirect:</literal> prefix allows you to <para>The special <literal>redirect:</literal> prefix allows you to
accomplish this. If a view name is returned that has the prefix accomplish this. If a view name is returned that has the prefix
redirect:, then <classname>UrlBasedViewResolver</classname> (and all <literal>redirect:</literal>, the <classname>UrlBasedViewResolver</classname> (and all
subclasses) recognize this as a special indication that a redirect is subclasses) will recognize this as a special indication that a redirect is
needed. The rest of the view name will be treated as the redirect needed. The rest of the view name will be treated as the redirect
URL.</para> URL.</para>
@ -2029,7 +2055,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
such as <literal>redirect:/my/response/controller.html</literal> will such as <literal>redirect:/my/response/controller.html</literal> will
redirect relative to the current servlet context, while a name such as redirect relative to the current servlet context, while a name such as
<literal>redirect:http://myhost.com/some/arbitrary/path.html</literal> <literal>redirect:http://myhost.com/some/arbitrary/path.html</literal>
will redirect to an absolute URL. The important thing is that as long will redirect to an absolute URL. The important thing is that, as long
as this redirect view name is injected into the controller like any as this redirect view name is injected into the controller like any
other logical view name, the controller is not even aware that other logical view name, the controller is not even aware that
redirection is happening.</para> redirection is happening.</para>
@ -2040,12 +2066,12 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<para>It is also possible to use a special <literal>forward:</literal> <para>It is also possible to use a special <literal>forward:</literal>
prefix for view names that are ultimately resolved by prefix for view names that are ultimately resolved by
<classname>UrlBasedViewResolver</classname> and subclasses. All this <classname>UrlBasedViewResolver</classname> and subclasses. This
does is create an <classname>InternalResourceView</classname> (which creates an <classname>InternalResourceView</classname> (which
ultimately does a <literal>RequestDispatcher.forward()</literal>) ultimately does a <literal>RequestDispatcher.forward()</literal>)
around the rest of the view name, which is considered a URL. around the rest of the view name, which is considered a URL.
Therefore, this prefix is not useful with Therefore, this prefix is not useful with
<classname>InternalResourceViewResolver</classname> / <classname>InternalResourceViewResolver</classname> and
<classname>InternalResourceView</classname> (for JSPs for example). <classname>InternalResourceView</classname> (for JSPs for example).
But the prefix can be helpful when you are primarily using another But the prefix can be helpful when you are primarily using another
view technology, but still want to force a forward of a resource to be view technology, but still want to force a forward of a resource to be
@ -2053,7 +2079,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
multiple view resolvers, instead.)<!--I think the preceding sentences were a bit garbled. I tried to reword a bit. And is this paragraph logical?--></para> multiple view resolvers, instead.)<!--I think the preceding sentences were a bit garbled. I tried to reword a bit. And is this paragraph logical?--></para>
<para>As with the <literal>redirect:</literal> prefix, if the view <para>As with the <literal>redirect:</literal> prefix, if the view
name with the prefix is just injected into the controller, the name with the <literal>forward:</literal> prefix is injected into the controller, the
controller does not detect that anything special is happening in terms controller does not detect that anything special is happening in terms
of handling the response.<!--Can you reword to clarify the point? The controller does not detect what?--></para> of handling the response.<!--Can you reword to clarify the point? The controller does not detect what?--></para>
</section> </section>
@ -2063,7 +2089,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<title><classname>ContentNegotiatingViewResolver</classname></title> <title><classname>ContentNegotiatingViewResolver</classname></title>
<para>The <classname>ContentNegotiatingViewResolver</classname> does not <para>The <classname>ContentNegotiatingViewResolver</classname> does not
resolve views itself, but rather delegates to other view resolvers, resolve views itself but rather delegates to other view resolvers,
selecting the view that resembles the representation requested by the selecting the view that resembles the representation requested by the
client. Two strategies exist for a client to request a representation client. Two strategies exist for a client to request a representation
from the server:</para> from the server:</para>
@ -2088,7 +2114,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
types</ulink> that it understands. For example, an HTTP request for types</ulink> that it understands. For example, an HTTP request for
<literal>http://www.example.com/users/fred</literal> with an <literal>http://www.example.com/users/fred</literal> with an
<literal>Accept</literal> header set to <literal>application/pdf <literal>Accept</literal> header set to <literal>application/pdf
</literal>requests a PDF representation of the user fred while </literal>requests a PDF representation of the user fred, while
<literal>http://www.example.com/users/fred</literal> with an <literal>http://www.example.com/users/fred</literal> with an
<literal>Accept</literal> header set to <literal>text/xml</literal> <literal>Accept</literal> header set to <literal>text/xml</literal>
requests an XML representation. This strategy is known as <ulink requests an XML representation. This strategy is known as <ulink
@ -2098,22 +2124,22 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
</itemizedlist> </itemizedlist>
<note> <note>
<para>One issue with the Accept header is that is impossible to change <para>One issue with the <literal>Accept</literal> header is that it is
it in a web browser, in HTML. For example, in Firefox, it is fixed impossible to set it in a web browser within HTML. For example, in Firefox, it is fixed
to<!--So how would you set the Accept header as in second bullet, if you can't do it in html? Indicate?--></para> to:
<!--So how would you set the Accept header as in second bullet, if you can't do it in html? Indicate?--></para>
<programlisting>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 <programlisting>Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</programlisting>
</programlisting>
<para>For this reason it is common to see the use of a distinct URI <para>For this reason it is common to see the use of a distinct URI
for each representation.</para> for each representation when developing browser based web applications.</para>
</note> </note>
<para>To support multiple representations of a resource, Spring provides <para>To support multiple representations of a resource, Spring provides
the <classname>ContentNegotiatingViewResolver</classname> to resolve a the <classname>ContentNegotiatingViewResolver</classname> to resolve a
view based on the file extension or <literal>Accept</literal> header of view based on the file extension or <literal>Accept</literal> header of
the HTTP request. <classname>ContentNegotiatingViewResolver</classname> the HTTP request. <classname>ContentNegotiatingViewResolver</classname>
does not perform the view resolution itself, but instead delegates to a does not perform the view resolution itself but instead delegates to a
list of view resolvers that you specify through the bean property list of view resolvers that you specify through the bean property
<literal>ViewResolvers</literal>.<!--A human has to specify this list of resolvers, right? See example below.--></para> <literal>ViewResolvers</literal>.<!--A human has to specify this list of resolvers, right? See example below.--></para>
@ -2129,10 +2155,10 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
example text/*, in which case a <classname>View</classname> whose example text/*, in which case a <classname>View</classname> whose
Context-Type was text/xml is a compatible match.</para> Context-Type was text/xml is a compatible match.</para>
<para>To support the resolution of a view based on a file extension, you <para>To support the resolution of a view based on a file extension,
use the <classname>ContentNegotiatingViewResolver </classname>bean use the <classname>ContentNegotiatingViewResolver </classname>bean
property <literal>MediaTypes</literal> to specify a mapping of file property <literal>mediaTypes</literal> to specify a mapping of file
extensions to media types. For more information on the algorithm to extensions to media types. For more information on the algorithm used to
determine the request media type, refer to the API documentation for determine the request media type, refer to the API documentation for
<classname>ContentNegotiatingViewResolver</classname>.</para> <classname>ContentNegotiatingViewResolver</classname>.</para>
@ -2161,7 +2187,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
&lt;bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/&gt;</programlisting> &lt;bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/&gt;</programlisting>
<para>The <classname>InternalResourceViewResolver</classname> handles <para>The <classname>InternalResourceViewResolver</classname> handles
the translation of view names and JSP pages while the the translation of view names and JSP pages, while the
<classname>BeanNameViewResolver</classname> returns a view based on the <classname>BeanNameViewResolver</classname> returns a view based on the
name of a bean. (See "<link name of a bean. (See "<link
linkend="mvc-viewresolver-resolver">Resolving views with the linkend="mvc-viewresolver-resolver">Resolving views with the
@ -2172,22 +2198,22 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
feed. For more information on creating an Atom Feed representation, see feed. For more information on creating an Atom Feed representation, see
the section Atom Views.<!--Need a correct link or x-ref re the preceding sentence.I couldn't find an "Atom Views" section.--></para> the section Atom Views.<!--Need a correct link or x-ref re the preceding sentence.I couldn't find an "Atom Views" section.--></para>
<para>In this configuration, if a request is made with an .html <para>In the above configuration, if a request is made with an <literal>.html</literal>
extension, the view resolver looks for a view that matches the text/html extension, the view resolver looks for a view that matches the <literal>text/html</literal>
media type. The <classname>InternalResourceViewResolver</classname> media type. The <classname>InternalResourceViewResolver</classname>
provides the matching view for text/html. If the request is made with provides the matching view for <literal>text/html</literal>. If the request is made with
the file extension .atom, the view resolver looks for a view that the file extension <literal>.atom</literal>, the view resolver looks for a view that
matches the application/atom+xml media type. This view is provided by matches the <literal>application/atom+xml</literal> media type. This view is provided by
the <classname>BeanNameViewResolver</classname> that maps to the the <classname>BeanNameViewResolver</classname> that maps to the
<classname>SampleContentAtomView</classname> if the view name returned <classname>SampleContentAtomView</classname> if the view name returned
is <classname>content</classname>. Alternatively, client requests can be is <classname>content</classname>. Alternatively, client requests can be
made without a file extension and setting the Accept header to the made without a file extension but with the <literal>Accept</literal> header set to the
preferred media-type and the same resolution of request to views would preferred media-type, and the same resolution of request to views would
occur.<!--Can you reword preceding sentence? I don't follow it.--></para> occur.<!--Can you reword preceding sentence? I don't follow it.--></para>
<note> <note>
<para>If <classname>ContentNegotiatingViewResolver</classname>'s list <para>If <classname>ContentNegotiatingViewResolver</classname>'s list
of ViewResolvers is not configured explicitly, then it automatically of ViewResolvers is not configured explicitly, it automatically
uses any ViewResolvers defined in the application context.</para> uses any ViewResolvers defined in the application context.</para>
</note> </note>
@ -2195,22 +2221,22 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
for a URI of the form <literal>http://localhost/content.atom</literal> for a URI of the form <literal>http://localhost/content.atom</literal>
or <literal>http://localhost/content</literal> with an or <literal>http://localhost/content</literal> with an
<literal>Accept</literal> header of application/atom+xml is shown <literal>Accept</literal> header of application/atom+xml is shown
below</para> below.</para>
<programlisting language="java">@Controller <programlisting language="java">@Controller
public class ContentController { public class ContentController {
private List&lt;SampleContent&gt; contentList = new ArrayList&lt;SampleContent&gt;(); private List&lt;SampleContent&gt; contentList = new ArrayList&lt;SampleContent&gt;();
@RequestMapping(value="/content", method=RequestMethod.GET) @RequestMapping(value="/content", method=RequestMethod.GET)
public ModelAndView getContent() { public ModelAndView getContent() {
ModelAndView mav = new ModelAndView(); ModelAndView mav = new ModelAndView();
mav.setViewName("content"); mav.setViewName("content");
mav.addObject("sampleContentList", contentList); mav.addObject("sampleContentList", contentList);
return mav; return mav;
} }
}</programlisting> }</programlisting>
</section> </section>
</section> </section>
@ -2224,19 +2250,19 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<interfacename>LocaleResolver</interfacename> objects.</para> <interfacename>LocaleResolver</interfacename> objects.</para>
<para>When a request comes in, the <para>When a request comes in, the
<classname>DispatcherServlet</classname> looks for a locale resolver and <classname>DispatcherServlet</classname> looks for a locale resolver, and
if it finds one it tries to use it to set the locale. Using the if it finds one it tries to use it to set the locale. Using the
<literal>RequestContext.getLocale()</literal> method, you can always <literal>RequestContext.getLocale()</literal> method, you can always
retrieve the locale that was resolved by the locale resolver.</para> retrieve the locale that was resolved by the locale resolver.</para>
<para>Besides the automatic locale resolution, you can also attach an <para>In addition to automatic locale resolution, you can also attach an
interceptor to the handler mapping (see <xref interceptor to the handler mapping (see <xref
linkend="mvc-handlermapping-interceptor" /> for more information on linkend="mvc-handlermapping-interceptor" /> for more information on
handler mapping interceptors), to change the locale under specific handler mapping interceptors) to change the locale under specific
circumstances, based on a parameter in the request, for example.</para> circumstances, for example, based on a parameter in the request.</para>
<para>Locale resolvers and interceptors are all defined in the <para>Locale resolvers and interceptors are defined in the
<literal>org.springframework.web.servlet.i18n</literal> package, and are <literal>org.springframework.web.servlet.i18n</literal> package and are
configured in your application context in the normal way. Here is a configured in your application context in the normal way. Here is a
selection of the locale resolvers included in Spring.</para> selection of the locale resolvers included in Spring.</para>
@ -2245,7 +2271,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<para>This locale resolver inspects the <para>This locale resolver inspects the
<literal>accept-language</literal> header in the request that was sent <literal>accept-language</literal> header in the request that was sent
by the browser of the client. Usually this header field contains the by the client (e.g., a web browser). Usually this header field contains the
locale of the client's operating system.</para> locale of the client's operating system.</para>
</section> </section>
@ -2253,11 +2279,10 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<title><classname>CookieLocaleResolver</classname></title> <title><classname>CookieLocaleResolver</classname></title>
<para>This locale resolver inspects a <classname>Cookie</classname> that <para>This locale resolver inspects a <classname>Cookie</classname> that
might exist on the client, to see if a locale is specified. If so, it might exist on the client to see if a locale is specified. If so, it
uses that specific locale. Using the properties of this locale resolver, uses the specified locale. Using the properties of this locale resolver,
you can specify the name of the cookie, as well as the maximum age. Find you can specify the name of the cookie as well as the maximum age. Find
below an example of defining a below an example of defining a <classname>CookieLocaleResolver</classname>.</para>
<classname>CookieLocaleResolver</classname>.</para>
<programlisting language="xml">&lt;bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"&gt; <programlisting language="xml">&lt;bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"&gt;
@ -2303,7 +2328,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<entry>Integer.MAX_INT</entry> <entry>Integer.MAX_INT</entry>
<entry>The maximum time a cookie will stay persistent on the <entry>The maximum time a cookie will stay persistent on the
client. If -1 is specified, the cookie will not be persisted. It client. If -1 is specified, the cookie will not be persisted; it
will only be available until the client shuts down his or her will only be available until the client shuts down his or her
browser.</entry> browser.</entry>
</row> </row>
@ -2314,9 +2339,8 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<entry>/</entry> <entry>/</entry>
<entry>Limits the visibility of the cookie to a certain part of <entry>Limits the visibility of the cookie to a certain part of
your site.. When cookiePath is your site. When cookiePath is specified, the cookie will only
specified, the cookie will only be visible to that path, and the be visible to that path and the paths below it.</entry>
paths below it.</entry>
</row> </row>
</tbody> </tbody>
</tgroup> </tgroup>
@ -2334,7 +2358,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<section id="mvc-localeresolver-interceptor"> <section id="mvc-localeresolver-interceptor">
<title><classname>LocaleChangeInterceptor</classname></title> <title><classname>LocaleChangeInterceptor</classname></title>
<para>You can build in changing of locales by adding the <para>You can enable changing of locales by adding the
<classname>LocaleChangeInterceptor</classname> to one of the handler <classname>LocaleChangeInterceptor</classname> to one of the handler
mappings (see <xref linkend="mvc-handlermapping" />). It will detect a mappings (see <xref linkend="mvc-handlermapping" />). It will detect a
parameter in the request and change the locale. It calls parameter in the request and change the locale. It calls
@ -2397,7 +2421,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter {
<classname>ResourceBundleThemeSource</classname>, you can register a <classname>ResourceBundleThemeSource</classname>, you can register a
bean in the application context with the reserved name bean in the application context with the reserved name
<classname>themeSource</classname>. The web application context <classname>themeSource</classname>. The web application context
automatically detects that bean and starts using it.</para> automatically detects a bean with that name and uses it.</para>
<para>When using the <classname>ResourceBundleThemeSource</classname>, a <para>When using the <classname>ResourceBundleThemeSource</classname>, a
theme is defined in a simple properties file. <!--Revise preceding sentence to clarify: To use ResourceBundleThemeSource, you define a theme in a properties file? OR do you mean a theme--><!--is already defined in a simple properties file for use with ResourceBundleThemeSource?-->The theme is defined in a simple properties file. <!--Revise preceding sentence to clarify: To use ResourceBundleThemeSource, you define a theme in a properties file? OR do you mean a theme--><!--is already defined in a simple properties file for use with ResourceBundleThemeSource?-->The
@ -2419,14 +2443,14 @@ background=/themes/cool/img/coolBg.jpg</programlisting>
&lt;head&gt; &lt;head&gt;
&lt;link rel="stylesheet" href="&lt;spring:theme code="styleSheet"/&gt;" type="text/css"/&gt; &lt;link rel="stylesheet" href="&lt;spring:theme code="styleSheet"/&gt;" type="text/css"/&gt;
&lt;/head&gt; &lt;/head&gt;
&lt;body background="&lt;spring:theme code="background"/&gt;"&gt; &lt;body style="background=&lt;spring:theme code="background"/&gt;"&gt;
... ...
&lt;/body&gt; &lt;/body&gt;
&lt;/html&gt;</programlisting> &lt;/html&gt;</programlisting>
<para>By default, the <classname>ResourceBundleThemeSource</classname> <para>By default, the <classname>ResourceBundleThemeSource</classname>
uses an empty base name prefix. As a result, the properties files are uses an empty base name prefix. As a result, the properties files are
loaded from the root of the classpath, so you would put the loaded from the root of the classpath. Thus you would put the
<literal>cool.properties</literal> theme definition in a directory at <literal>cool.properties</literal> theme definition in a directory at
the root of the classpath, for example, in the root of the classpath, for example, in
<literal>/WEB-INF/classes</literal>. The <literal>/WEB-INF/classes</literal>. The
@ -2485,15 +2509,14 @@ background=/themes/cool/img/coolBg.jpg</programlisting>
<row> <row>
<entry><classname>CookieThemeResolver</classname></entry> <entry><classname>CookieThemeResolver</classname></entry>
<entry>The selected theme is stored in a cookie on the <entry>The selected theme is stored in a cookie on the client.</entry>
user-agent's machine.</entry>
</row> </row>
</tbody> </tbody>
</tgroup> </tgroup>
</table> </table>
<para>Spring also provides a <para>Spring also provides a
<classname>ThemeChangeInterceptor</classname>, which allows theme <classname>ThemeChangeInterceptor</classname> that allows theme
changes on every request with a simple request parameter.<!--Do you need more info or an example re preceding sentence?--></para> changes on every request with a simple request parameter.<!--Do you need more info or an example re preceding sentence?--></para>
</section> </section>
</section> </section>