java 在 Spring 应用程序中实现自定义验证的最佳方法是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32349479/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is the best way to implement custom validation in spring application?
提问by imbond
I'm (new in spring development) creating REST API for my application, CRUD operations are implemented successfully but now I want to implement server side validation. I've also read that there are several ways through which validation could be implemented.
我(春季开发新手)为我的应用程序创建 REST API,CRUD 操作已成功实现,但现在我想实现服务器端验证。我还读到有几种方法可以实现验证。
- Using given annotations -> @notempty, @email, etc...
- Using custom validation -> extending validators
- 使用给定的注释 -> @notempty、@email 等...
- 使用自定义验证 -> 扩展验证器
I want to implement both of them in my application, With reference to that,
我想在我的应用程序中实现它们,参考,
is it good approach to follow?
这是遵循的好方法吗?
OR
或者
Is there any other ways through which validation can be implemented?
有没有其他方法可以实现验证?
Controller
控制器
@RestController
public class EmployeeController {
@Autowired
DataServices dataServices;
@Autowired
EmployeeValidator employeeValidator;
@InitBinder
protected void initBinder(WebDataBinder binder) {
binder.addValidators(employeeValidator);
}
@RequestMapping(value = "/employee/", method = RequestMethod.POST)
public ResponseEntity<Object> createUser(
@Valid @RequestBody Employee employee,
UriComponentsBuilder ucBuilder) throws Exception,
DataIntegrityViolationException {
if (dataServices.addEmployee(employee) == 0) {
Error error = new Error(1, "Data integrity violation",
"Email id is already exists.");
return new ResponseEntity<Object>(error, HttpStatus.CONFLICT);
}
HttpHeaders headers = new HttpHeaders();
headers.setLocation(ucBuilder.path("/employee/{id}")
.buildAndExpand(employee.getId()).toUri());
Status status = new Status(1, "Employee has been added successfully.");
return new ResponseEntity<Object>(status, headers, HttpStatus.CREATED);
}
}
Error Handler
错误处理程序
@ControllerAdvice
public class RestErrorHandler {
private static final Logger logger = Logger
.getLogger(RestErrorHandler.class);
private MessageSource messageSource;
@Autowired
public RestErrorHandler(MessageSource messageSource) {
this.messageSource = messageSource;
}
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
@ResponseBody
public ValidationErrorDTO processValidationError(
MethodArgumentNotValidException ex) {
logger.debug("Handling form validation error");
BindingResult result = ex.getBindingResult();
List<FieldError> fieldErrors = result.getFieldErrors();
return processFieldErrors(fieldErrors);
}
private ValidationErrorDTO processFieldErrors(List<FieldError> fieldErrors) {
ValidationErrorDTO dto = new ValidationErrorDTO();
for (FieldError fieldError : fieldErrors) {
String localizedErrorMessage = resolveLocalizedErrorMessage(fieldError);
dto.addFieldError(fieldError.getField(), localizedErrorMessage,
fieldError.getDefaultMessage());
}
return dto;
}
private String resolveLocalizedErrorMessage(FieldError fieldError) {
Locale currentLocale = LocaleContextHolder.getLocale();
String localizedErrorMessage = messageSource.getMessage(fieldError,
currentLocale);
// If a message was not found, return the most accurate field error code
// instead.
// You can remove this check if you prefer to get the default error
// message.
if (localizedErrorMessage.equals(fieldError.getDefaultMessage())) {
String[] fieldErrorCodes = fieldError.getCodes();
localizedErrorMessage = fieldErrorCodes[0];
}
return localizedErrorMessage;
}
}
Validator
验证器
@Component
public class EmployeeValidator implements Validator {
public boolean supports(Class clazz) {
return Employee.class.isAssignableFrom(clazz);
}
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "firstName", errors
.getFieldError().getCode(), "First name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "lastName", errors
.getFieldError().getCode(),
"Last name is required.");
ValidationUtils.rejectIfEmptyOrWhitespace(errors, "email", errors
.getFieldError().getCode(),
"Email is required.");
}
}
Model
模型
@Entity
@Table(name = "employee")
@JsonIgnoreProperties({ "hibernateLazyInitializer", "handler" })
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue
@Column(name = "id")
private long id;
// @NotEmpty(message = "Please enter first name")
@Column(name = "first_name")
private String firstName;
// @NotEmpty(message = "Please enter last name")
@Column(name = "last_name")
private String lastName;
// @NotEmpty(message = "Please enter email address")
@Email(message = "Please enter valid email address")
@Column(name = "email", unique = true)
private String email;
@NotEmpty(message = "Please enter mobile number")
@Size(min = 10, message = "Please enter valid mobile number")
@Column(name = "phone")
private String phone;
//Getter and Setter
}
采纳答案by Ricardo Vila
In your aproach you are using Server side validations but only in the controller layer. Have you tryied to use Bussines layer validations, like Hibernate Validation API http://hibernate.org/validator/
在您的方法中,您使用的是服务器端验证,但仅在控制器层中使用。您是否尝试过使用业务层验证,例如 Hibernate Validation API http://hibernate.org/validator/
I've used it in a recent project and form me it's a great way to keep data consistent. Some tweaks and utils were needed to make it work as we wanted but it was not too difficult. For example, this validations, by default, are only checked just after persisting a Object in database, but in our controller we needed to make this validations earlier, so you we had to implement a way to call validation mechanism that relies on hibernate validation mechanism. Or, as another example, we had to develop a similar system on a web service to return errors when incoming data was not valid.
我在最近的一个项目中使用了它,我认为这是保持数据一致的好方法。需要进行一些调整和实用程序才能使其按我们想要的方式工作,但这并不太难。例如,默认情况下,此验证仅在将 Object 持久化到数据库后才进行检查,但在我们的控制器中,我们需要更早地进行此验证,因此您必须实现一种方法来调用依赖于休眠验证机制的验证机制. 或者,作为另一个例子,我们必须在 Web 服务上开发一个类似的系统,以便在传入的数据无效时返回错误。
One way to use validations when needed is to implement it on all your bussines objects. They can inherit for a class like this:
在需要时使用验证的一种方法是在所有业务对象上实现它。他们可以继承这样的类:
import java.io.Serializable;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import javax.xml.bind.annotation.XmlTransient;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.fasterxml.Hymanson.annotation.JsonIgnore;
public abstract class BusinessObject implements Serializable, IObjectWithReport, IBusinessObject {
private static Log log = LogFactory.getLog(BusinessObject.class.getName());
private final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
@JsonIgnore
private Set<ConstraintViolation<BusinessObject>> errors;
/* Validation methods */
public final boolean valid() {
preValidate();
errors = new HashSet<ConstraintViolation<BusinessObject>>();
errors = validator.validate(this);
postValidate();
return errors.isEmpty();
}
/**
* Method to be overwriten in subclases so any BO can make some arrangement before checking valid
*/
protected void preValidate() {
log.trace("Generic prevalidate of " + this.getClass().getName());
}
/**
* Method to be overwriten in subclases so any BO can make some arrangement once validation has been made
*/
protected void postValidate() {
log.trace("Generic postValidate of " + this.getClass().getName());
}
public Set<ConstraintViolation<BusinessObject>> getErrors() {
return errors;
}
public boolean hasErrors() {
return errors != null && !errors.isEmpty();
}
}
Note that i use standard javax.validation.Validation API (check references here JPA 2.0 : what is javax.validation.* package?). But the implementation i use is the one from Hibernate.
请注意,我使用标准 javax.validation.Validation API(在此处检查参考文献JPA 2.0:什么是 javax.validation.* 包?)。但是我使用的实现是来自 Hibernate 的实现。
Pros:
优点:
- Validations are placed in one single layer, not spread along various layers. So they are easier to maintain.
- Better model consistency because of that data is always validated in the same way, independently of how it was generated (user input, web service, pulled from other systems, etc).
- 验证放置在一个单层中,而不是沿着各个层展开。所以它们更容易维护。
- 更好的模型一致性,因为数据总是以相同的方式验证,独立于它的生成方式(用户输入、Web 服务、从其他系统提取等)。
Cons:
缺点:
- You need to develop some utils so you can use Model Validations in other layers, but it's not very dificult.
- May be overkill if you have a simple project, whithout complexities like many info sources (user input, webservices input, rest services, other database systemas, etc) or interactions.
- 您需要开发一些实用程序,以便您可以在其他层中使用模型验证,但这并不是很困难。
- 如果您有一个简单的项目,而没有像许多信息源(用户输入、Web 服务输入、休息服务、其他数据库系统等)或交互那样的复杂性,则可能有点过分。