Prevent Unintended Field Updates in SpringBoot: DTOs vs @InitBinder
This article explains how SpringBoot's default data binding can unintentionally modify unwanted fields and demonstrates three safe approaches—using a dedicated DTO, configuring WebDataBinder with @InitBinder to allow only specific fields, and applying advanced @InitBinder options such as required fields, constructor‑only binding, and custom validators—to ensure precise and secure request parameter handling.
1. Introduction
In SpringBoot, request parameters are bound to model objects by default, which can unintentionally update fields that should not be modified. The article demonstrates this risk with a User entity containing age , name , password , and email .
<code>public class User {
private Integer age;
private String name;
private String password;
private String email;
// getters and setters
}</code>A controller method receives a User instance and updates it via MyBatis:
<code>@GetMapping("/updateBasicInfo")
public R updateAgeAndEmail(User user) {
// update operation
userMapper.updateUserInfo(user);
return R.success("更新成功");
}</code>Without restricting binding, a client could supply values for name or password , leading to unintended changes.
2. Solutions
2.1 Use a DTO
Create a DTO that contains only the fields that should be updatable, then copy its values to the User entity in the service layer.
<code>public class UserBasicDTO {
private Integer age;
private String email;
// getters and setters
}</code>Change the controller to accept the DTO:
<code>@GetMapping("/updateBasicInfo")
public R updateAgeAndEmail(UserBasicDTO dto) {
// copy dto to User and update
}</code>2.2 Use @InitBinder
Configure a WebDataBinder to allow only specific fields:
<code>@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setAllowedFields("age", "email");
}</code>Alternatively, disallow fields:
<code>binder.setDisallowedFields("name", "password");</code>For constructor‑only binding, enable declarative binding:
<code>@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setDeclarativeBinding(true);
}</code>Define a constructor in the entity that accepts only the allowed parameters:
<code>public static class User {
private Integer age;
private String email;
public User(Integer age, String email) {
this.age = age;
this.email = email;
}
}</code>If multiple constructors exist, Spring will raise an error (illustrated in the following image):
Additional @InitBinder configurations:
Require certain fields:
<code>@InitBinder
public void initBinder(WebDataBinder binder) {
binder.setRequiredFields("id");
}</code>If the required field is missing, Spring throws an exception (shown below):
Custom validators can also be added:
<code>@InitBinder
public void initBinder(WebDataBinder binder) {
binder.addValidators(new UserValidator());
}
public class UserValidator implements Validator {
public boolean supports(Class<?> clazz) {
return User.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
User user = (User) target;
if (user.getAge() < 0) {
errors.reject("user.error.age", "年龄错误");
}
}
}</code>When validation fails, Spring returns a binding exception (illustrated in the image):
Spring Full-Stack Practical Cases
Full-stack Java development with Vue 2/3 front-end suite; hands-on examples and source code analysis for Spring, Spring Boot 2/3, and Spring Cloud.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.