This question is very similar to this one, but I dont know where to start.
Suppose I have an action like this:
@GetMapping("/foo/{id}")
public Collection<Foo> listById(@PathVariable("id") string id) {
return null;
}
How could one intercept the listById
method and change the value of id
(Eg.: concat a string, pad with zeros etc)?
My scenario is that mostly of the IDs are left-padded with zeros (lengths differ) and I dont want to leave this to my ajax calls.
Expected solution:
@GetMapping("/foo/{id}")
public Collection<Foo> listById(@PathVariablePad("id", 4) string id) {
// id would be "0004" on "/foo/4" calls
return null;
}
Ok, here is how I've done it.
Since we can't inherit annotations and thus @PathVariable
's target are only parameters, we have to create a new annotation, as follows:
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface PathVariablePad {
int zeros() default 0;
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
Now we need to create a HandlerMethodArgumentResolver
. In this case, since all I want is to left-pad a @PathVariable
with zeros, we're going to inherit PathVariableMethodArgumentResolver
, like this:
public class PathVariablePadderMethodArgumentResolver extends PathVariableMethodArgumentResolver {
private String leftPadWithZeros(Object target, int zeros) {
return String.format("%1$" + zeros + "s", target.toString()).replace(' ', '0'); // Eeeewwwwwwwwwwww!
}
@Override
public boolean supportsParameter(MethodParameter parameter) {
return parameter.hasParameterAnnotation(PathVariablePad.class);
}
@Override
protected NamedValueInfo createNamedValueInfo(MethodParameter parameter) {
PathVariablePad pvp = parameter.getParameterAnnotation(PathVariablePad.class);
return new NamedValueInfo(pvp.name(), pvp.required(), leftPadWithZeros("", pvp.zeros()));
}
@Override
protected Object resolveName(String name, MethodParameter parameter, NativeWebRequest request) throws Exception {
PathVariablePad pvp = parameter.getParameterAnnotation(PathVariablePad.class);
return leftPadWithZeros(super.resolveName(name, parameter, request), pvp.zeros());
}
}
Finally, let's register our method argument resolver (xml):
<mvc:annotation-driven>
<mvc:argument-resolvers>
<bean class="my.package.PathVariablePadderMethodArgumentResolver" />
</mvc:argument-resolvers>
</mvc:annotation-driven>
The usage is pretty simple and heres how to do this:
@GetMapping("/ten/{id}")
public void ten(@PathVariablePad(zeros = 10) String id) {
// id would be "0000000001" on "/ten/1" calls
}
@GetMapping("/five/{id}")
public void five(@PathVariablePad(zeros = 5) String id) {
// id would be "00001" on "/five/1" calls
}