|
|
|
@ -1295,8 +1295,8 @@ public void handle(@RequestBody String body, Writer writer) throws IOException { |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-ann-responsebody"> |
|
|
|
<section id="mvc-ann-responsebody"> |
|
|
|
<title>Mapping the response body with the @ResponseBody |
|
|
|
<title>Mapping the response body with the @ResponseBody |
|
|
|
annotation</title> |
|
|
|
annotation</title> |
|
|
|
|
|
|
|
|
|
|
|
<para>Similar to <interfacename>@RequestBody</interfacename>, there is |
|
|
|
<para>Similar to <interfacename>@RequestBody</interfacename>, there is |
|
|
|
the <interfacename>@ResponseBody</interfacename> annotation. This |
|
|
|
the <interfacename>@ResponseBody</interfacename> annotation. This |
|
|
|
@ -1550,15 +1550,15 @@ public class MyFormController { |
|
|
|
<section id="mvc-handlermapping"> |
|
|
|
<section id="mvc-handlermapping"> |
|
|
|
<title>Handler mappings</title> |
|
|
|
<title>Handler mappings</title> |
|
|
|
|
|
|
|
|
|
|
|
<para>In previous versions of Spring MVC, users were required to define <interfacename>HandlerMapping</interfacename>s in |
|
|
|
<para>In previous versions of Spring MVC, users were required to define |
|
|
|
the web application context |
|
|
|
<interfacename>HandlerMapping</interfacename>s in the web application |
|
|
|
to map incoming web requests to |
|
|
|
context to map incoming web requests to appropriate handlers. With the |
|
|
|
appropriate handlers. With the introduction of Spring 2.5, the <classname>DispatcherServlet</classname> enables the |
|
|
|
introduction of Spring 2.5, the <classname>DispatcherServlet</classname> |
|
|
|
<classname>DefaultAnnotationHandlerMapping</classname>, which looks for |
|
|
|
enables the <classname>DefaultAnnotationHandlerMapping</classname>, which |
|
|
|
<interfacename>@RequestMapping</interfacename> annotations on <interfacename>@Controllers</interfacename>. |
|
|
|
looks for <interfacename>@RequestMapping</interfacename> annotations on |
|
|
|
Typically, you do not need to override this default mapping, except when overriding the properties. |
|
|
|
<interfacename>@Controllers</interfacename>. Typically, you do not need to |
|
|
|
These properties are: |
|
|
|
override this default mapping, except when overriding the properties. |
|
|
|
</para> |
|
|
|
These properties are:</para> |
|
|
|
|
|
|
|
|
|
|
|
<itemizedlist spacing="compact"> |
|
|
|
<itemizedlist spacing="compact"> |
|
|
|
<listitem> |
|
|
|
<listitem> |
|
|
|
@ -1615,16 +1615,17 @@ public class MyFormController { |
|
|
|
subclasses of |
|
|
|
subclasses of |
|
|
|
<classname>org.springframework.web.servlet.handler.AbstractUrlHandlerMapping</classname></emphasis>).</para> |
|
|
|
<classname>org.springframework.web.servlet.handler.AbstractUrlHandlerMapping</classname></emphasis>).</para> |
|
|
|
|
|
|
|
|
|
|
|
<para>The following example shows how to override the default mapping, and add an interceptor:</para> |
|
|
|
<para>The following example shows how to override the default mapping, and |
|
|
|
|
|
|
|
add an interceptor:</para> |
|
|
|
|
|
|
|
|
|
|
|
<programlisting language="xml"><![CDATA[<beans> |
|
|
|
<programlisting language="xml"><beans> |
|
|
|
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> |
|
|
|
<bean id="handlerMapping" class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"> |
|
|
|
<property name="interceptors"> |
|
|
|
<property name="interceptors"> |
|
|
|
<bean class="example.MyInterceptor"/> |
|
|
|
<bean class="example.MyInterceptor"/> |
|
|
|
</property> |
|
|
|
</property> |
|
|
|
</bean> |
|
|
|
</bean> |
|
|
|
|
|
|
|
|
|
|
|
<beans>]]></programlisting> |
|
|
|
<beans></programlisting> |
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-handlermapping-interceptor"> |
|
|
|
<section id="mvc-handlermapping-interceptor"> |
|
|
|
<title>Intercepting requests - the |
|
|
|
<title>Intercepting requests - the |
|
|
|
@ -1841,10 +1842,11 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { |
|
|
|
<row> |
|
|
|
<row> |
|
|
|
<entry><classname>ContentNegotiatingViewResolver</classname></entry> |
|
|
|
<entry><classname>ContentNegotiatingViewResolver</classname></entry> |
|
|
|
|
|
|
|
|
|
|
|
<entry>An implementation of the <interfacename>ViewResolver</interfacename> |
|
|
|
<entry>An implementation of the |
|
|
|
interface that that resolves a view based on the request file name or <literal>Accept</literal> header. |
|
|
|
<interfacename>ViewResolver</interfacename> interface that that |
|
|
|
See <xref linkend="mvc-multiple-representations"/>. |
|
|
|
resolves a view based on the request file name or |
|
|
|
</entry> |
|
|
|
<literal>Accept</literal> header. See <xref |
|
|
|
|
|
|
|
linkend="mvc-multiple-representations" />.</entry> |
|
|
|
</row> |
|
|
|
</row> |
|
|
|
</tbody> |
|
|
|
</tbody> |
|
|
|
</tgroup> |
|
|
|
</tgroup> |
|
|
|
@ -2068,139 +2070,138 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-multiple-representations"> |
|
|
|
<section id="mvc-multiple-representations"> |
|
|
|
<title><classname>ContentNegotiatingViewResolver</classname></title> |
|
|
|
<title><classname>ContentNegotiatingViewResolver</classname></title> |
|
|
|
|
|
|
|
|
|
|
|
<para>The <classname>ContentNegotiatingViewResolver</classname> |
|
|
|
<para>The <classname>ContentNegotiatingViewResolver</classname> does not |
|
|
|
does not resolve views itself, but rather delegates to other |
|
|
|
resolve views itself, but rather delegates to other view resolvers, |
|
|
|
view resolvers, selecting the view that resembles the representation |
|
|
|
selecting the view that resembles the representation requested by the |
|
|
|
requested by the client. |
|
|
|
client. There are two strategies for a client to inform the server of |
|
|
|
There are two strategies for a client to inform the server of |
|
|
|
the representation it is interested in receiving.</para> |
|
|
|
the representation it is interested in receiving.</para> |
|
|
|
|
|
|
|
|
|
|
|
<para>The first strategy is to use a distinct URI for each resource. |
|
|
|
<para>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. |
|
|
|
This is typically done by using a different file extension in the URI. |
|
|
|
For example the URI<literal> |
|
|
|
For example the URI<literal> |
|
|
|
http://www.example.com/users/fred.pdf</literal> requests a PDF |
|
|
|
http://www.example.com/users/fred.pdf</literal> requests a PDF |
|
|
|
representation of the user fred while |
|
|
|
representation of the user fred while |
|
|
|
<literal>http://www.example.com/users/fred.xml</literal> requests an XML |
|
|
|
<literal>http://www.example.com/users/fred.xml</literal> requests an XML |
|
|
|
representation.</para> |
|
|
|
representation.</para> |
|
|
|
|
|
|
|
|
|
|
|
<para>The second strategy is for the client to use the same URI to |
|
|
|
<para>The second strategy is for the client to use the same URI to |
|
|
|
locate the resource but set the <literal>Accept</literal> HTTP request |
|
|
|
locate the resource but set the <literal>Accept</literal> HTTP request |
|
|
|
header to list the <ulink |
|
|
|
header to list the <ulink |
|
|
|
url="http://en.wikipedia.org/wiki/Internet_media_type">media |
|
|
|
url="http://en.wikipedia.org/wiki/Internet_media_type">media |
|
|
|
types</ulink> that it understands. For example, a HTTP request for |
|
|
|
types</ulink> that it understands. For example, a 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 |
|
|
|
url="http://en.wikipedia.org/wiki/Content_negotiation">content |
|
|
|
url="http://en.wikipedia.org/wiki/Content_negotiation">content |
|
|
|
negotiation</ulink>.</para> |
|
|
|
negotiation</ulink>.</para> |
|
|
|
|
|
|
|
|
|
|
|
<note> |
|
|
|
<note> |
|
|
|
<para>One issue with the Accept header is that is impossible to change |
|
|
|
<para>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 |
|
|
|
it in a web browser, in HTML. For instance, in Firefox, it's fixed |
|
|
|
to</para> |
|
|
|
to</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.</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 set using the bean property |
|
|
|
list of view resolvers set using the bean property |
|
|
|
<literal>ViewResolvers</literal>.</para> |
|
|
|
<literal>ViewResolvers</literal>.</para> |
|
|
|
|
|
|
|
|
|
|
|
<para>The <classname>ContentNegotiatingViewResolver</classname> selects |
|
|
|
<para>The <classname>ContentNegotiatingViewResolver</classname> selects |
|
|
|
an appropriate <classname>View</classname> to handle the request by |
|
|
|
an appropriate <classname>View</classname> to handle the request by |
|
|
|
comparing the request media type(s) with the media type (a.k.a. |
|
|
|
comparing the request media type(s) with the media type (a.k.a. |
|
|
|
<literal>Content-Type</literal>) supported by the |
|
|
|
<literal>Content-Type</literal>) supported by the |
|
|
|
<classname>View</classname> associated with each of its |
|
|
|
<classname>View</classname> associated with each of its |
|
|
|
<classname>ViewResolvers</classname>. The first |
|
|
|
<classname>ViewResolvers</classname>. The first |
|
|
|
<classname>View</classname> in the list that has a compatible |
|
|
|
<classname>View</classname> in the list that has a compatible |
|
|
|
<literal>Content-Type</literal> is used to return the representation to |
|
|
|
<literal>Content-Type</literal> is used to return the representation to |
|
|
|
the client. The <literal>Accept</literal> header may include wild cards, |
|
|
|
the client. The <literal>Accept</literal> header may include wild cards, |
|
|
|
for example 'text/*', in which case a <classname>View</classname> whose |
|
|
|
for 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, |
|
|
|
<para>To support the resolution of a view based on a file extension, |
|
|
|
<classname>ContentNegotiatingViewResolver</classname>'s bean property |
|
|
|
<classname>ContentNegotiatingViewResolver</classname>'s bean property |
|
|
|
<literal>MediaTypes</literal> is used to specify a mapping of file |
|
|
|
<literal>MediaTypes</literal> is used 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 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> |
|
|
|
|
|
|
|
|
|
|
|
<para>Here is an example configuration of a |
|
|
|
<para>Here is an example configuration of a |
|
|
|
<classname>ContentNegotiatingViewResolver</classname></para> |
|
|
|
<classname>ContentNegotiatingViewResolver</classname></para> |
|
|
|
|
|
|
|
|
|
|
|
<programlisting language="xml"><![CDATA[<bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> |
|
|
|
<programlisting language="xml"><bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver"> |
|
|
|
<property name="mediaTypes"> |
|
|
|
<property name="mediaTypes"> |
|
|
|
<map> |
|
|
|
<map> |
|
|
|
<entry key="atom" value="application/atom+xml"/> |
|
|
|
<entry key="atom" value="application/atom+xml"/> |
|
|
|
<entry key="html" value="text/html"/> |
|
|
|
<entry key="html" value="text/html"/> |
|
|
|
</map> |
|
|
|
</map> |
|
|
|
</property> |
|
|
|
</property> |
|
|
|
<property name="viewResolvers"> |
|
|
|
<property name="viewResolvers"> |
|
|
|
<list> |
|
|
|
<list> |
|
|
|
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> |
|
|
|
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/> |
|
|
|
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
|
|
|
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> |
|
|
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
|
|
<property name="prefix" value="/WEB-INF/jsp/"/> |
|
|
|
<property name="suffix" value=".jsp"/> |
|
|
|
<property name="suffix" value=".jsp"/> |
|
|
|
</bean> |
|
|
|
</bean> |
|
|
|
</list> |
|
|
|
</list> |
|
|
|
</property> |
|
|
|
</property> |
|
|
|
</bean> |
|
|
|
</bean> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/>]]></programlisting> |
|
|
|
<bean id="content" class="com.springsource.samples.rest.SampleContentAtomView"/></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 - the ViewResolver |
|
|
|
linkend="mvc-viewresolver-resolver">Resolving views - the ViewResolver |
|
|
|
interface</link>" for more details on how Spring looks up and |
|
|
|
interface</link>" for more details on how Spring looks up and |
|
|
|
instantiates a view.) In this example, the <literal>content</literal> |
|
|
|
instantiates a view.) In this example, the <literal>content</literal> |
|
|
|
bean is a class that inherits from |
|
|
|
bean is a class that inherits from |
|
|
|
<classname>AbstractAtomFeedView</classname> which returns an Atom RSS |
|
|
|
<classname>AbstractAtomFeedView</classname> which returns an Atom RSS |
|
|
|
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'.</para> |
|
|
|
the section 'Atom Views'.</para> |
|
|
|
|
|
|
|
|
|
|
|
<para>In this configuration, if a request is made with a .html extension |
|
|
|
<para>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 |
|
|
|
the view resolver will look for a view that matches the text/html media |
|
|
|
type. The <classname>InternalResourceViewResolver</classname> provides |
|
|
|
type. The <classname>InternalResourceViewResolver</classname> provides |
|
|
|
the matching view for text/html. If the request is made with the file |
|
|
|
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 |
|
|
|
extension .atom, the view resolver will look for a view that matches the |
|
|
|
application/atom+xml media type. This view is provided by the |
|
|
|
application/atom+xml media type. This view is provided by the |
|
|
|
<classname>BeanNameViewResolver</classname> that maps to 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 'content'. Alternatively, client requests could be made without a |
|
|
|
is 'content'. Alternatively, client requests could be made without a |
|
|
|
file extension and setting the Accept header to the preferred media-type |
|
|
|
file extension and setting the Accept header to the preferred media-type |
|
|
|
and the same resolution of request to views would occur.</para> |
|
|
|
and the same resolution of request to views would occur.</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 will |
|
|
|
of ViewResolvers is not configured explicitly, then it will |
|
|
|
automatically use any ViewResolvers defined in the application |
|
|
|
automatically use any ViewResolvers defined in the application |
|
|
|
context.</para> |
|
|
|
context.</para> |
|
|
|
</note> |
|
|
|
</note> |
|
|
|
|
|
|
|
|
|
|
|
<para>The corresponding controller code that returns an Atom RSS feed |
|
|
|
<para>The corresponding controller code that returns an Atom RSS feed |
|
|
|
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<SampleContent> contentList = new ArrayList<SampleContent>(); |
|
|
|
private List<SampleContent> contentList = new ArrayList<SampleContent>(); |
|
|
|
@ -2214,9 +2215,7 @@ public class TimeBasedAccessInterceptor extends HandlerInterceptorAdapter { |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
}</programlisting> |
|
|
|
}</programlisting> |
|
|
|
|
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-localeresolver"> |
|
|
|
<section id="mvc-localeresolver"> |
|
|
|
@ -2813,9 +2812,11 @@ public class FileUploadBean { |
|
|
|
<section id="mvc-ann-exceptionhandler"> |
|
|
|
<section id="mvc-ann-exceptionhandler"> |
|
|
|
<title><interfacename>@ExceptionResolver</interfacename></title> |
|
|
|
<title><interfacename>@ExceptionResolver</interfacename></title> |
|
|
|
|
|
|
|
|
|
|
|
<para>As an alternative to implementing the <interfacename>HandlerExceptionResolver</interfacename>, you |
|
|
|
<para>As an alternative to implementing the |
|
|
|
can use the <interfacename>@ExceptionHandler</interfacename>. The <classname>@ExceptionHandler</classname> method annotation is |
|
|
|
<interfacename>HandlerExceptionResolver</interfacename>, you can use the |
|
|
|
used within a controller to specify which method will be invoked when an |
|
|
|
<interfacename>@ExceptionHandler</interfacename>. The |
|
|
|
|
|
|
|
<classname>@ExceptionHandler</classname> method annotation is used |
|
|
|
|
|
|
|
within a controller to specify which method will be invoked when an |
|
|
|
exception of a specific type is thrown during the execution of |
|
|
|
exception of a specific type is thrown during the execution of |
|
|
|
controller methods. For example</para> |
|
|
|
controller methods. For example</para> |
|
|
|
|
|
|
|
|
|
|
|
@ -2851,7 +2852,6 @@ public class SimpleController { |
|
|
|
<classname>ModelAndView</classname> object. Please refer to the API |
|
|
|
<classname>ModelAndView</classname> object. Please refer to the API |
|
|
|
documentation for more details.</para> |
|
|
|
documentation for more details.</para> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-coc"> |
|
|
|
<section id="mvc-coc"> |
|
|
|
@ -3189,6 +3189,49 @@ public class SimpleController { |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-etag"> |
|
|
|
|
|
|
|
<title>ETag support</title> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<para>An <ulink url="http://en.wikipedia.org/wiki/HTTP_ETag">ETag</ulink> |
|
|
|
|
|
|
|
(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 |
|
|
|
|
|
|
|
<literal>Last-Modified</literal> header. When a server returns a |
|
|
|
|
|
|
|
representation with an ETag header, the client can use this header in |
|
|
|
|
|
|
|
subsequent GETs, in an <literal>If-None-Match</literal> header. If the |
|
|
|
|
|
|
|
content has not changed, the server will return <literal>304: Not |
|
|
|
|
|
|
|
Modified</literal>.</para> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<para>Support for ETags is provided by the servlet filter |
|
|
|
|
|
|
|
<classname>ShallowEtagHeaderFilter</classname>. Since it is a plain |
|
|
|
|
|
|
|
Servlet Filter, and thus can be used in combination with any web |
|
|
|
|
|
|
|
framework. The <classname>ShallowEtagHeaderFilter</classname> filter |
|
|
|
|
|
|
|
creates so-called shallow ETags (as opposed to 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 an MD5 hash over |
|
|
|
|
|
|
|
that, and returns that as an ETag header in the response. The next time a |
|
|
|
|
|
|
|
client sends a request for the same resource, it uses that hash as the |
|
|
|
|
|
|
|
<literal>If-None-Match</literal> value. The filter notices this, renders |
|
|
|
|
|
|
|
the view again, and compares the two hashes. If they are equal, a |
|
|
|
|
|
|
|
<literal>304</literal> 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 bandwidth, as the rendered response is not sent |
|
|
|
|
|
|
|
back over the wire.</para> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<para>You configure the <classname>ShallowEtagHeaderFilter</classname> in |
|
|
|
|
|
|
|
<filename>web.xml</filename>:</para> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<programlisting language="xml"><filter> |
|
|
|
|
|
|
|
<filter-name>etagFilter</filter-name> |
|
|
|
|
|
|
|
<filter-class>org.springframework.web.filter.ShallowEtagHeaderFilter</filter-class> |
|
|
|
|
|
|
|
</filter> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<filter-mapping> |
|
|
|
|
|
|
|
<filter-name>etagFilter</filter-name> |
|
|
|
|
|
|
|
<servlet-name>petclinic</servlet-name> |
|
|
|
|
|
|
|
</filter-mapping></programlisting> |
|
|
|
|
|
|
|
</section> |
|
|
|
|
|
|
|
|
|
|
|
<section id="mvc-resources"> |
|
|
|
<section id="mvc-resources"> |
|
|
|
<title>Further Resources</title> |
|
|
|
<title>Further Resources</title> |
|
|
|
|
|
|
|
|
|
|
|
|