DTO:
public class User {
@NotNull
private String name;
@NotNull
private String password;
//..
}
Controller:
One way to do it is adding message in @NotNull annotation on entity properties. And adding @Valid annotation in controller request body.
DTO:
public class User {
@NotNull(message = "User name cannot be empty")
private String name;
@NotNull(message = "Password cannot be empty")
private String password;
//..
}
Controller:
@RequestMapping(value = "/user", method = RequestMethod.POST)
public ResponseEntity<String> saveUser(@Valid @RequestBody User user) {
//..
return new ResponseEntity<>(HttpStatus.OK);
}
// Add one
@ExceptionHandler(MethodArgumentNotValidException.class)
public ResponseEntity<List<YourErrorResponse>> handleException(MethodArgumentNotValidException ex) {
// Loop through FieldErrors in ex.getBindingResult();
// return *YourErrorReponse* filled using *fieldErrors*
}
For customized the error message in JSON format then do the below steps.
- Create one @Component called CommonErrorHandler
@Component
public class CommonErrorHandler {
public Map<String,Object> getFieldErrorResponse(BindingResult result){
Map<String, Object> fielderror = new HashMap<>();
List<FieldError>errors= result.getFieldErrors();
for (FieldError error : errors) {
fielderror.put(error.getField(), error.getDefaultMessage());
}return fielderror;
}
public ResponseEntity<Object> fieldErrorResponse(String message,Object fieldError){
Map<String, Object> map = new HashMap<>();
map.put("isSuccess", false);
map.put("data", null);
map.put("status", HttpStatus.BAD_REQUEST);
map.put("message", message);
map.put("timeStamp", DateUtils.getSysDate());
map.put("filedError", fieldError);
return new ResponseEntity<Object>(map,HttpStatus.BAD_REQUEST);
}
}
-- Add InvalidException class
public class InvalidDataException extends RuntimeException {
/**
* @author Ashok Parmar
*/
private static final long serialVersionUID = -4164793146536667139L;
private BindingResult result;
public InvalidDataException(BindingResult result) {
super();
this.setResult(result);
}
public BindingResult getResult() {
return result;
}
public void setResult(BindingResult result) {
this.result = result;
}
}
- Introduce @ControllerAdvice class
@ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
@ExceptionHandler(InvalidDataException.class)
public ResponseEntity<?> invalidDataException(InvalidDataException ex, WebRequest request) {
List<FieldError> errors = ex.getResult().getFieldErrors();
for (FieldError error : errors) {
logger.error("Filed Name ::: " + error.getField() + "Error Message :::" + error.getDefaultMessage());
}
return commonErrorHandler.fieldErrorResponse("Error", commonErrorHandler.getFieldErrorResponse(ex.getResult()));
}
}
-- Use in controller with @Valid and throw exception
public AnyBeans update(**@Valid** @RequestBody AnyBeans anyBeans ,
BindingResult result) {
AnyBeans resultStr = null;
if (result.hasErrors()) {
**throw new InvalidDataException(result);**
} else {
resultStr = anyBeansService.(anyBeans );
return resultStr;
}
}
-- Output will be in JSON format
{
"timeStamp": 1590500231932,
"data": null,
"message": "Error",
"isSuccess": false,
"status": "BAD_REQUEST",
"filedError": {
"name": "Name is mandatory"
}
}
Hope this will be work. :-D
You can do something like this
@ExceptionHandler(value = MethodArgumentNotValidException.class)
protected ResponseEntity<Error> handleGlobalExceptions(MethodArgumentNotValidException ex,
WebRequest request) {
log.catching(ex);
return new ResponseEntity<>(createErrorResp(HttpStatus.BAD_REQUEST,
ex.getBindingResult().getFieldErrors().stream().map(err -> err.getDefaultMessage())
.collect(java.util.stream.Collectors.joining(", "))),
HttpStatus.BAD_REQUEST);
}
{
"message": "string",
"errors": [
{
"field": "string",
"message": "string",
"error_code": "string"
}
],
"resource": "string",
"description": "string"
}
Add some information too.
If you use just @Valid
, you need to catch BindException
. If you use @Valid @RequestBody
catch MethodArgumentNotValidException
Some sources:
HandlerMethodArgumentResolverComposite.getArgumentResolver(MethodParameter parameter):129
- search which HandlerMethodArgumentResolver support such parameter
RequestResponseBodyMethodProcessor.supportsParameter(MethodParameter parameter)
- return true if parameter has annotation @RequestBody
RequestResponseBodyMethodProcessor:139
- throw MethodArgumentNotValidException
ModelAttributeMethodProcessor:164
- throw BindException
@ControllerAdvice(annotations = RestController.class)
public class GlobalExceptionHandler implements ApplicationContextAware {
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.OK)
@ResponseBody
public ApplicationError validationException(MethodArgumentNotValidException e) {
e.printStackTrace();
return new ApplicationError(SysMessageEnum.MSG_005, e.getBindingResult().getAllErrors().get(0).getDefaultMessage());
}
}