SPR-7746 Add examples of using regex in URI template vars and working with 'Last-Modified' HTTP requests
This commit is contained in:
parent
efaa941672
commit
df5e4d6d56
|
|
@ -103,11 +103,14 @@ import org.springframework.web.bind.support.WebBindingInitializer;
|
||||||
import org.springframework.web.context.request.NativeWebRequest;
|
import org.springframework.web.context.request.NativeWebRequest;
|
||||||
import org.springframework.web.context.request.RequestScope;
|
import org.springframework.web.context.request.RequestScope;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
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.multipart.MultipartRequest;
|
||||||
import org.springframework.web.servlet.HandlerAdapter;
|
import org.springframework.web.servlet.HandlerAdapter;
|
||||||
import org.springframework.web.servlet.HandlerMapping;
|
import org.springframework.web.servlet.HandlerMapping;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
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.InternalPathMethodNameResolver;
|
||||||
import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver;
|
import org.springframework.web.servlet.mvc.multiaction.MethodNameResolver;
|
||||||
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
import org.springframework.web.servlet.mvc.multiaction.NoSuchRequestHandlingMethodException;
|
||||||
|
|
@ -440,6 +443,13 @@ public class AnnotationMethodHandlerAdapter extends WebContentGenerator
|
||||||
return mav;
|
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) {
|
public long getLastModified(HttpServletRequest request, Object handler) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,7 @@ import org.springframework.web.bind.support.WebArgumentResolver;
|
||||||
import org.springframework.web.bind.support.WebBindingInitializer;
|
import org.springframework.web.bind.support.WebBindingInitializer;
|
||||||
import org.springframework.web.bind.support.WebDataBinderFactory;
|
import org.springframework.web.bind.support.WebDataBinderFactory;
|
||||||
import org.springframework.web.context.request.ServletWebRequest;
|
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.HandlerMethod;
|
||||||
import org.springframework.web.method.HandlerMethodSelector;
|
import org.springframework.web.method.HandlerMethodSelector;
|
||||||
import org.springframework.web.method.annotation.ModelFactory;
|
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.method.support.ModelAndViewContainer;
|
||||||
import org.springframework.web.servlet.ModelAndView;
|
import org.springframework.web.servlet.ModelAndView;
|
||||||
import org.springframework.web.servlet.View;
|
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.annotation.ModelAndViewResolver;
|
||||||
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
|
import org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter;
|
||||||
import org.springframework.web.servlet.mvc.method.annotation.support.DefaultMethodReturnValueHandler;
|
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()));
|
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
|
@Override
|
||||||
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
|
protected long getLastModifiedInternal(HttpServletRequest request, HandlerMethod handlerMethod) {
|
||||||
return -1;
|
return -1;
|
||||||
|
|
|
||||||
|
|
@ -790,7 +790,7 @@ public class ClinicController {
|
||||||
<title>URI Template Patterns</title>
|
<title>URI Template Patterns</title>
|
||||||
|
|
||||||
<para><emphasis>URI templates</emphasis> can be used for convenient access to selected
|
<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
|
<para>A URI Template is a URI-like string, containing one or more
|
||||||
variable names. When you substitute values for these variables, the
|
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
|
<emphasis>userId</emphasis>. Assigning the value <emphasis>fred</emphasis> to the variable
|
||||||
yields <code>http://www.example.com/users/fred</code>.</para>
|
yields <code>http://www.example.com/users/fred</code>.</para>
|
||||||
|
|
||||||
<para>In Spring MVC you can apply the <interfacename>@PathVariable</interfacename> annotation
|
<para>In Spring MVC you can use the <interfacename>@PathVariable</interfacename> annotation on
|
||||||
to a method argument to indicate that it is bound to the value of a URI template variable:</para>
|
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)
|
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
|
||||||
public String findOwner(<emphasis role="bold">@PathVariable</emphasis> String ownerId, Model model) {
|
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>"
|
<para>The URI Template "<literal>/owners/{ownerId}</literal>"
|
||||||
specifies the variable name <literal>ownerId</literal>. When the
|
specifies the variable name <literal>ownerId</literal>. When the
|
||||||
controller handles this request, the value of <literal>ownerId</literal>
|
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
|
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>
|
<tip>
|
||||||
<para>The matching of method parameter names to URI Template variable
|
<para>To process the @PathVariable annotation, Spring MVC needs to find the
|
||||||
names can only be done if your code is compiled with debugging
|
matching URI template variable by name. You can specify it in the annotation:</para>
|
||||||
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>
|
|
||||||
|
|
||||||
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
|
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}", method=RequestMethod.GET)
|
||||||
public String findOwner(<emphasis role="bold">@PathVariable</emphasis>("ownerId") String theOwner, Model model) {
|
public String findOwner(<emphasis role="bold">@PathVariable</emphasis>("ownerId") String theOwner, Model model) {
|
||||||
// implementation omitted
|
// implementation omitted
|
||||||
}</programlisting>
|
}</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>
|
</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)
|
<programlisting language="java">@RequestMapping(value="/owners/{ownerId}/pets/{petId}", method=RequestMethod.GET)
|
||||||
public String findPet(<emphasis role="bold">@PathVariable</emphasis> String ownerId, <emphasis
|
public String findPet(<emphasis role="bold">@PathVariable</emphasis> String ownerId, <emphasis
|
||||||
|
|
@ -865,16 +866,38 @@ public class RelativePathUriTemplateController {
|
||||||
}
|
}
|
||||||
</programlisting>
|
</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.
|
<emphasis role="bold">any simple type</emphasis> such as int, long, Date, etc.
|
||||||
Spring automatically converts to the appropriate type or throws a
|
Spring automatically converts to the appropriate type or throws a
|
||||||
<classname>TypeMismatchException</classname> if it fails to do so.
|
<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" />.
|
See <xref linkend="mvc-ann-typeconversion"/> and <xref linkend="mvc-ann-webdatabinder" />.
|
||||||
</para>
|
</para>
|
||||||
|
|
||||||
</section>
|
</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">
|
<section id="mvc-ann-requestmapping-patterns">
|
||||||
<title>Path Patterns</title>
|
<title>Path Patterns</title>
|
||||||
|
|
||||||
|
|
@ -1737,6 +1760,43 @@ public class MyFormController {
|
||||||
</programlisting>
|
</programlisting>
|
||||||
</section>
|
</section>
|
||||||
</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>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue