SPR-7746 Add examples of using regex in URI template vars and working with 'Last-Modified' HTTP requests

This commit is contained in:
Rossen Stoyanchev 2011-06-23 15:28:57 +00:00
parent efaa941672
commit df5e4d6d56
3 changed files with 98 additions and 20 deletions

View File

@ -103,11 +103,14 @@ import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.RequestScope;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.multipart.MultipartRequest;
import org.springframework.web.servlet.HandlerAdapter;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.LastModified;
import org.springframework.web.servlet.mvc.multiaction.InternalPathMethodNameResolver;
import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver;
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
@ -440,6 +443,13 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
return mav;
}
/**
* This method always returns -1 since an annotated controller can have many methods, each
* requiring separate lastModified calculations. Instead an @{@link RequestMapping} method
* can calculate the lastModified value, call {@link WebRequest#checkNotModified(long)} to
* check it, and return {@code null} if that returns {@code true}.
* @see WebRequest#checkNotModified(long)
*/
public long getLastModified(HttpServletRequest request, Object handler) {
return -1;
}

View File

@ -56,6 +56,7 @@ import org.springframework.web.bind.support.WebArgumentResolver;
import org.springframework.web.bind.support.WebBindingInitializer;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.ServletWebRequest;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.method.HandlerMethodSelector;
import org.springframework.web.method.annotation.ModelFactory;
@ -76,6 +77,7 @@ import org.springframework.web.method.support.InvocableHandlerMethod;
import org.springframework.web.method.support.ModelAndViewContainer;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.View;
import org.springframework.web.servlet.mvc.LastModified;
import org.springframework.web.servlet.mvc.annotation.ModelAndViewResolver;
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
@ -432,6 +434,12 @@ public class RequestMappingHandlerAdapter extends AbstractHandlerMethodAdapter i
Void.TYPE.equals(methodReturnType.getParameterType()));
}
/**
* This method always returns -1 since {@link HandlerMethod} does not implement {@link LastModified}.
* Instead an @{@link RequestMapping} method, calculate the lastModified value, and call
* {@link WebRequest#checkNotModified(long)}, and return {@code null} if that returns {@code true}.
* @see WebRequest#checkNotModified(long)
*/
@Override
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
return -1;

View File

@ -790,7 +790,7 @@ public class ClinicController {
<title>URI Template Patterns</title>
<para><emphasis>URI templates</emphasis> can be used for convenient access to selected
segments of a URL in a <interfacename>@RequestMapping</interfacename> method.</para>
parts of a URL in a <interfacename>@RequestMapping</interfacename> method.</para>
<para>A URI Template is a URI-like string, containing one or more
variable names. When you substitute values for these variables, the
@ -801,8 +801,8 @@ public class ClinicController {
<emphasis>userId</emphasis>. Assigning the value <emphasis>fred</emphasis> to the variable
yields <code>http://www.example.com/users/fred</code>.</para>
<para>In Spring MVC you can apply the <interfacename>@PathVariable</interfacename> annotation
to a method argument to indicate that it is bound to the value of a URI template variable:</para>
<para>In Spring MVC you can use the <interfacename>@PathVariable</interfacename> annotation on
a method argument to bind it to the value of a URI template variable:</para>
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(<emphasis role="bold">@PathVariable</emphasis> String ownerId, Model model) {
@ -815,30 +815,31 @@ public String findOwner(<emphasis role="bold">@PathVariable</emphasis> String ow
<para>The URI Template "<literal>/owners/{ownerId}</literal>"
specifies the variable name <literal>ownerId</literal>. When the
controller handles this request, the value of <literal>ownerId</literal>
is set to the value found in the appropriate segment of the URI.
is set to the value found in the appropriate part of the URI.
For example, when a request comes in for <code>/owners/fred</code>, the value
<literal>fred</literal> is bound to the <literal>ownerId</literal> method argument.</para>
of <literal>ownerId</literal> is <literal>fred</literal>.</para>
<tip>
<para>The matching of method parameter names to URI Template variable
names can only be done if your code is compiled with debugging
enabled. This is normally the case, however, if you do not have debugging enabled,
you will need to specify the name of the URI Template variable as follows:</para>
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(<emphasis role="bold">@PathVariable</emphasis>("ownerId") String ownerId, Model model) {
// implementation omitted
}</programlisting>
<para>You can do the same if you want the names of URI template variable and the method argument to differ:</para>
<para>To process the @PathVariable annotation, Spring MVC needs to find the
matching URI template variable by name. You can specify it in the annotation:</para>
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(<emphasis role="bold">@PathVariable</emphasis>("ownerId") String theOwner, Model model) {
// implementation omitted
}</programlisting>
<para>Or if the URI template variable name matches the method argument name
you can omit that detail. As long as your code is not compiled without debugging
information, Spring MVC will match the method argument name to the URI template variable name:</para>
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
public String findOwner(<emphasis role="bold">@PathVariable</emphasis> String ownerId, Model model) {
// implementation omitted
}</programlisting>
</tip>
<para>A method can have multiple <interfacename>@PathVariable</interfacename> annotations:</para>
<para>A method can have any number of <interfacename>@PathVariable</interfacename> annotations:</para>
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
public String findPet(<emphasis role="bold">@PathVariable</emphasis> String ownerId, <emphasis
@ -865,16 +866,38 @@ public class RelativePathUriTemplateController {
}
</programlisting>
<para><interfacename>@PathVariable</interfacename> method arguments can be of
<para>A <interfacename>@PathVariable</interfacename> argument can be of
<emphasis role="bold">any simple type</emphasis> such as int, long, Date, etc.
Spring automatically converts to the appropriate type or throws a
<classname>TypeMismatchException</classname> if it fails to do so.
This type conversion process can be customized through a data binder.
You can also register support for parsing additional data types.
See <xref linkend="mvc-ann-typeconversion"/> and <xref linkend="mvc-ann-webdatabinder" />.
</para>
</section>
<section id="mvc-ann-requestmapping-uri-templates-regex">
<title>URI Template Patterns with Regular Expressions</title>
<para>Sometimes you need more precision in defining URI template variables.
Consider the URL <code>"/spring-web/spring-web-3.0.5.jar"</code>.
How do you break it down into multiple parts?</para>
<para>The <interfacename>@RequestMapping</interfacename> annotation supports the
use of regular expressions in URI template variables.
The syntax is <code>{varName:regex}</code> where the first part defines the
variable name and the second - the regular expression.For example:</para>
<programlisting language="java">
@RequestMapping("/spring-web/{symbolicName:[a-z-]+}-{version:\d\.\d\.\d}.{extension:\.[a-z]}")
public void handle(@PathVariable String version, @PathVariable String extension) {
// ...
}
}
</programlisting>
</section>
<section id="mvc-ann-requestmapping-patterns">
<title>Path Patterns</title>
@ -1737,6 +1760,43 @@ public class MyFormController {
</programlisting>
</section>
</section>
<section id="mvc-ann-lastmodified">
<title>Support for the 'Last-Modified' Response Header To Facilitate Content Caching</title>
<para>An <interfacename>@RequestMapping</interfacename> method may wish
to support <literal>'Last-Modified'</literal> HTTP requests,
as defined in the contract for the Servlet API's
<literal>getLastModified</literal> method, to facilitate content caching.
This involves calculating a lastModified <literal>long</literal>
value for a given request, comparing it against the
<literal>'If-Modified-Since'</literal> request header value, and
potentially returning a response with status code 304 (Not Modified).
An annotated controller method can achieve that as follows:</para>
<programlisting language="java">
@RequestMapping
public String myHandleMethod(WebRequest webRequest, Model model) {
long lastModified = // 1. application-specific calculation
if (request.checkNotModified(lastModified)) {
// 2. shortcut exit - no further processing necessary
return null;
}
// 3. or otherwise further request processing, actually preparing content
model.addAttribute(...);
return "myViewName";
}
</programlisting>
<para>There are two key elements to note:
calling <code>request.checkNotModified(lastModified)</code>
and returning <literal>null</literal>. The former sets
the response status to 304 before it returns <literal>true</literal>.
The latter, in combination with the former, causes
Spring MVC to do no further processing of the request.
</para>
</section>
</section>
</section>