Moved REST Etag filter to MVC chapter
git-svn-id: https://src.springframework.org/svn/spring-framework/trunk@1390 50f2f4bb-b051-0410-bef5-90022cba6387
This commit is contained in:
parent
90379c5172
commit
dbca532155
|
|
@ -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>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue