In Spring-mvc the attribute names in view have to always match the property names in model?
forms, spring-mvc
Solution
Use @RequestParam annotation in @InitBinder annotated method, and set the desired value manually.
UserController
@InitBinder(value="user")
public void bind(WebDataBinder dataBinder, WebRequest webRequest, @RequestParam(value="pass", required=false) String password) {
User user = (User) dataBinder.getTarget();
user.setPassword(password);
}
Is there a way I can use annotation to handle the difference or I have to always match names?
AFAIK there is no ready-made annotation in Spring MVC that can resolve your problem; you need custom setup to handle the situation.
WebModelAttribute
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebModelAttribute {
String modelAttributeName();
WebParameterMapping[] parameterMappings();
}
WebParameterMapping
@Target({ElementType.METHOD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface WebParameterMapping {
String webProperty();
String beanProperty();
}
UserController
@Controller
public class UserController extends AbstractController {
@Override
@InitBinder(value="user")
@WebModelAttribute(modelAttributeName="user", parameterMappings={@WebParameterMapping(webProperty="pass", beanProperty="password")})
protected void bindWebParameters(WebDataBinder dataBinder, WebRequest webRequest, WebParameterResolver mappingResolver) {
super.bindWebParameters(dataBinder, webRequest, mappingResolver);
}
AbstractController
public class AbstractController {
protected void bindWebParameters(WebDataBinder dataBinder, WebRequest webRequest, WebParameterResolver mappingResolver) {
if(mappingResolver != null && dataBinder.getTarget() != null && dataBinder.getObjectName().equals(mappingResolver.getModelAttributeName())) {
String[] allowedFields = mappingResolver.getAllowedFields(dataBinder.getAllowedFields());
String[] disallowedFields = mappingResolver.getDisallowedFields(dataBinder.getDisallowedFields());
dataBinder.setAllowedFields(allowedFields);
dataBinder.setDisallowedFields(disallowedFields);
dataBinder.bind(mappingResolver.getPropertyValues(dataBinder, webRequest));
}
}
}
WebParameterResolver
public class WebParameterResolver {
private String modelAttributeName;
private WebParameterMapping[] parameterMappings;
public WebParameterResolver(String modelAttributeName,
WebParameterMapping[] parameterMappings) {
this.modelAttributeName = modelAttributeName;
this.parameterMappings = parameterMappings;
}
public String getModelAttributeName() {
return modelAttributeName;
}
public String[] getDisallowedFields(String[] existingDisallowedFields) {
List<String> disallowedFields = new ArrayList<String>();
for (WebParameterMapping parameterMapping : parameterMappings) {
disallowedFields.add(parameterMapping.webProperty());
}
if (existingDisallowedFields != null) {
for (String disallowedField : existingDisallowedFields) {
disallowedFields.add(disallowedField);
}
}
return disallowedFields.toArray(new String[disallowedFields.size()]);
}
public String[] getAllowedFields(String[] existingAllowedFields) {
List<String> allowedFields = new ArrayList<String>();
for (WebParameterMapping parameterMapping : parameterMappings) {
allowedFields.add(parameterMapping.beanProperty());
}
if (existingAllowedFields != null) {
for (String allowedField : existingAllowedFields) {
allowedFields.add(allowedField);
}
}
return allowedFields.toArray(new String[allowedFields.size()]);
}
public MutablePropertyValues getPropertyValues(WebDataBinder dataBinder,
WebRequest webRequest) {
MutablePropertyValues propertyValues = new MutablePropertyValues();
for (WebParameterMapping parameterMapping : parameterMappings) {
String[] values = webRequest.getParameterValues(parameterMapping.webProperty());
if (values == null || values.length == 0) {
// do nothing
} else if (values.length == 1) {
propertyValues.add(parameterMapping.beanProperty(), values[0]);
} else {
propertyValues.add(parameterMapping.beanProperty(), values);
}
}
dataBinder.bind(propertyValues);
return propertyValues;
}
}
CustomArgumentResolver
public class CustomArgumentResolver implements WebArgumentResolver {
@Override
public Object resolveArgument(MethodParameter methodParameter, NativeWebRequest webRequest) throws Exception {
if(methodParameter.getParameterType().equals(WebParameterResolver.class)) {
WebModelAttribute webModelAttribute = methodParameter.getMethod().getAnnotation(WebModelAttribute.class);
if(webModelAttribute == null) {
throw new RuntimeException("method must have WebModelAttribute");
}
return new WebParameterResolver(webModelAttribute.modelAttributeName(), webModelAttribute.parameterMappings());
}
return UNRESOLVED;
}
}
beans.xml
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="customArgumentResolvers" ref="timetracking.annotations.CustomArgumentResolver"/>
</bean>
<bean name="timetracking.annotations.CustomArgumentResolver"
class="timetracking.annotations.CustomArgumentResolver" />
You can also have a `public static void bindWebParameters(...)` method in some helper class; so you don't have to extend the AbstractController every time.
Problem
In the http request body, the way password string is passed is "pass=1111", however in the bean the way password is defined is ''private String password". Is there a way I can use annotation to handle the difference or I have to always match names? The Http request is like this ``` curl -H "Accept:text/html" -H "Content-Type application/x-www-form-urlencoded" -d 'email=test%40gmail.com&pass=1111&passconfirm=1111&name=x+y' "http://localhost:8080/project/register" ``` Handler method is ``` @RequestMapping(method = RequestMethod.POST, headers = "content-type=application/x-www-form-urlencoded") public String register(@ModelAttribute UserAccountBean account) ... ``` UserAccountBean is ``` public class UserAccountBean2 { @NotNull @Size(min = 1, max = 25) private String name; @NotNull @Size(min = 4, max = 8) private String password; @NotNull private String email; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String toString() { return new ToStringCreator(this).append("name", name).append("password", password).toString(); } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } } ```