Add "mvcUrl" function to Spring tag library

This commit adds a new function to the Spring tag library for preparing
links to @Controller methods. For more details see the Javadoc of
MvcUriComponentsBuilder.fromMappingName.

Issue: SPR-5779
This commit is contained in:
Rossen Stoyanchev 2014-06-10 18:11:11 -04:00
parent 2140648246
commit 6b129c52e3
3 changed files with 101 additions and 24 deletions

View File

@ -21,7 +21,20 @@ import org.springframework.web.method.HandlerMethod;
import java.lang.reflect.Method; import java.lang.reflect.Method;
/** /**
* A strategy for assigning a name to a controller method mapping. * A strategy for assigning a name to a handler method's mapping.
*
* <p>The strategy can be configured on
* {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping
* AbstractHandlerMethodMapping}. It is used to assign a name to the mapping of
* every registered handler method. The names can then be queried via
* {@link org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#getHandlerMethodsForMappingName(String)
* AbstractHandlerMethodMapping#getHandlerMethodsForMappingName}.
*
* <p>Applications can build a URL to a controller method by name with the help
* of the static method
* {@link org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder#fromMappingName(String)
* MvcUriComponentsBuilder#fromMappingName} or in JSPs through the "mvcUrl"
* function registered by the Spring tag library.
* *
* @author Rossen Stoyanchev * @author Rossen Stoyanchev
* @since 4.1 * @since 4.1

View File

@ -193,38 +193,62 @@ public class MvcUriComponentsBuilder extends UriComponentsBuilder {
} }
/** /**
* Create a {@link UriComponentsBuilder} from a request mapping identified * Create a URL from the name of a Spring MVC controller method's request mapping.
* by name. The configured *
* <p>The configured
* {@link org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy * {@link org.springframework.web.servlet.handler.HandlerMethodMappingNamingStrategy
* HandlerMethodMappingNamingStrategy} assigns a default name to every * HandlerMethodMappingNamingStrategy} determines the names of controller
* {@code @RequestMapping} method but an explicit name may also be assigned * method request mappings at startup. By default all mappings are assigned
* through the {@code @RequestMapping} name attribute. * a name based on the capital letters of the class name, followed by "#" as
* <p>This is intended for use in EL expressions, typically in JSPs or other * separator, and then the method name. For example "PC#getPerson"
* view templates, which can use the convenience method {@link #toUriString()}. * for a class named PersonController with method getPerson. In case the
* <p>The default naming convention for mappings is based on the capital * naming convention does not produce unique results, an explicit name may
* letters of the class name, followed by "#" as a separator, and the method * be assigned through the name attribute of the {@code @RequestMapping}
* name. For example "TC#getFoo" for a class named TestController with method * annotation.
* getFoo. Use explicit names where the naming convention does not produce *
* unique results. * <p>This is aimed primarily for use in view rendering technologies and EL
* @param name the mapping name * expressions. The Spring URL tag library registers this method as a function
* @param argumentValues argument values for the controller method; those values * called "mvcUrl".
* are important for {@code @RequestParam} and {@code @PathVariable} arguments *
* but may be passed as {@code null} otherwise. * <p>For example, given this controller:
* @return the UriComponentsBuilder * <pre class="code">
* &#064;RequestMapping("/people")
* class PersonController {
*
* &#064;RequestMapping("/{id}")
* public HttpEntity<Void> getPerson(&#064;PathVariable String id) { ... }
*
* }
* </pre>
*
* A JSP can prepare a URL to the controller method as follows:
*
* <pre class="code">
* <%@ taglib uri="http://www.springframework.org/tags" prefix="s" %>
*
* &lt;a href="${s:mvcUrl('PC#getPerson').arg(0,"123").build()}"&gt;Get Person&lt;/a&gt;
* </pre>
*
* <p>Note that it's not necessary to specify all arguments. Only the ones
* required to prepare the URL, mainly {@code @RequestParam} and {@code @PathVariable}).
*
* @param mappingName the mapping name
* @return a builder to to prepare the URI String
* @throws IllegalArgumentException if the mapping name is not found or * @throws IllegalArgumentException if the mapping name is not found or
* if there is no unique match * if there is no unique match
* @since 4.1 * @since 4.1
*/ */
public static UriComponentsBuilder fromMappingName(String name, Object... argumentValues) { public static MethodArgumentBuilder fromMappingName(String mappingName) {
RequestMappingInfoHandlerMapping hm = getRequestMappingInfoHandlerMapping(); RequestMappingInfoHandlerMapping handlerMapping = getRequestMappingInfoHandlerMapping();
List<HandlerMethod> handlerMethods = hm.getHandlerMethodsForMappingName(name); List<HandlerMethod> handlerMethods = handlerMapping.getHandlerMethodsForMappingName(mappingName);
if (handlerMethods == null) { if (handlerMethods == null) {
throw new IllegalArgumentException("Mapping name not found: " + name); throw new IllegalArgumentException("Mapping mappingName not found: " + mappingName);
} }
if (handlerMethods.size() != 1) { if (handlerMethods.size() != 1) {
throw new IllegalArgumentException("No unique match for mapping name " + name + ": " + handlerMethods); throw new IllegalArgumentException(
"No unique match for mapping mappingName " + mappingName + ": " + handlerMethods);
} }
return fromMethod(handlerMethods.get(0).getMethod(), argumentValues); return new MethodArgumentBuilder(handlerMethods.get(0).getMethod());
} }
/** /**
@ -455,4 +479,37 @@ public class MvcUriComponentsBuilder extends UriComponentsBuilder {
Object[] getArgumentValues(); Object[] getArgumentValues();
} }
public static class MethodArgumentBuilder {
private final Method method;
private final Object[] argumentValues;
public MethodArgumentBuilder(Method method) {
Assert.notNull(method, "'method' is required");
this.method = method;
this.argumentValues = new Object[method.getParameterTypes().length];
for (int i = 0; i < this.argumentValues.length; i++) {
this.argumentValues[i] = null;
}
}
public MethodArgumentBuilder arg(int index, Object value) {
this.argumentValues[index] = value;
return this;
}
public String build() {
return MvcUriComponentsBuilder.fromMethod(this.method, this.argumentValues)
.build(false).encode().toUriString();
}
public String buildAndExpand(Object... uriVariables) {
return MvcUriComponentsBuilder.fromMethod(this.method, this.argumentValues)
.build(false).expand(uriVariables).encode().toString();
}
}
} }

View File

@ -472,4 +472,11 @@
</attribute> </attribute>
</tag> </tag>
<function>
<description>Helps to prepare a URL to a Spring MVC controller method.</description>
<name>mvcUrl</name>
<function-class>org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder</function-class>
<function-signature>org.springframework.web.servlet.mvc.method.annotation.MvcUriComponentsBuilder.MethodArgumentBuilder fromMappingName(java.lang.String)</function-signature>
</function>
</taglib> </taglib>