Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In Spring-mvc the attribute names in view have to always match the property names in model?

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;
    }
}
like image 962
Bobo Avatar asked Feb 26 '23 15:02

Bobo


2 Answers

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.

like image 179
dira Avatar answered Feb 28 '23 05:02

dira


You can achieve it with this:

@RequestMapping(method = RequestMethod.POST, headers = "content-type=application/x-www-form-urlencoded")
public String register(@ModelAttribute("userAccountBean") UserAccountBean account) ...

@ModelAttribute("userAccountBean")
public UserAccountBean getUserAccountBean(HttpServletRequest req) {
  UserAccountBean uab = new UserAccountBean();
  uab.setPassword(req.getParameter("pass"));
  return uab;
}
like image 40
hleinone Avatar answered Feb 28 '23 03:02

hleinone