1) Add maven dependency as below
Pre-requisite
1
2
3
4
5
6
7
8
9
10
| < dependency > < groupId >org.hibernate</ groupId > < artifactId >hibernate-validator</ artifactId > < version >5.4.1.Final</ version > </ dependency > < dependency > < groupId >javax.validation</ groupId > < artifactId >validation-api</ artifactId > < version >2.0.0.Final</ version > </ dependency > |
2) Add the below snippet to the "WebMvcConfigurer" implementation class
WebMvcConfigurer-class
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
| import java.util.Locale; import org.springframework.context.MessageSource; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.ReloadableResourceBundleMessageSource; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.EnableWebMvc; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import org.springframework.web.servlet.i18n.SessionLocaleResolver; @Configuration @EnableWebMvc @ComponentScan (basePackages = { "com.test" }) public class WebMvcConfig implements WebMvcConfigurer { /** Loding .properties file from the calss path and contains the error codes **/ @Bean public MessageSource messageSource() { ReloadableResourceBundleMessageSource messageSource = new ReloadableResourceBundleMessageSource(); messageSource.setBasename( "classpath:messages" ); messageSource.setDefaultEncoding( "UTF-8" ); return messageSource; } /** Local validator object creation and binding message resource object ** to resolve the messages configured in the validation annoations **/ @Bean public LocalValidatorFactoryBean validator() { LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean(); bean.setValidationMessageSource(messageSource()); return bean; } /** Locale resolver **/ @Bean public LocaleResolver localeResolver() { SessionLocaleResolver localeResolver = new SessionLocaleResolver(); localeResolver.setDefaultLocale(Locale.US); return localeResolver; } @Bean public LocaleChangeInterceptor localeChangeInterceptor() { LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor(); localeChangeInterceptor.setParamName( "lang" ); return localeChangeInterceptor; } /** restering the locale change intercpeter to support I18N **/ @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(localeChangeInterceptor()); } /** method to overide the default behaviour of validator - required**/ @Override public org.springframework.validation.Validator getValidator() { return validator(); } } |
3) Extend the class "ResponseEntityExceptionHandler " and override all methods as below.
CustomizedResponseEntityExceptionHandler
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
| import java.util.ArrayList; import java.util.List; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import org.springframework.beans.TypeMismatchException; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; import org.springframework.http.HttpHeaders; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.FieldError; import org.springframework.validation.ObjectError; import org.springframework.web.HttpMediaTypeNotSupportedException; import org.springframework.web.HttpRequestMethodNotSupportedException; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.MissingServletRequestParameterException; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.context.request.WebRequest; import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException; import org.springframework.web.multipart.support.MissingServletRequestPartException; import org.springframework.web.servlet.NoHandlerFoundException; import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler; @ControllerAdvice @Order (Ordered.HIGHEST_PRECEDENCE) public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler { @Override protected ResponseEntity<Object> handleMethodArgumentNotValid( final MethodArgumentNotValidException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final List<String> errors = new ArrayList<>(); for ( final FieldError error : ex.getBindingResult().getFieldErrors()) { errors.add(error.getField() + ": " + error.getDefaultMessage()); } for ( final ObjectError error : ex.getBindingResult().getGlobalErrors()) { errors.add(error.getObjectName() + ": " + error.getDefaultMessage()); } final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors); return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request); } @Override protected ResponseEntity<Object> handleBindException(org.springframework.validation.BindException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { logger.info(ex.getClass().getName()); final List<String> errors = new ArrayList<>(); for ( final FieldError error : ex.getBindingResult().getFieldErrors()) { errors.add(error.getField() + ": " + error.getDefaultMessage()); } for ( final ObjectError error : ex.getBindingResult().getGlobalErrors()) { errors.add(error.getObjectName() + ": " + error.getDefaultMessage()); } final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors); return handleExceptionInternal(ex, apiError, headers, apiError.getStatus(), request); // return super.handleBindException(ex, headers, status, request); } @Override protected ResponseEntity<Object> handleTypeMismatch( final TypeMismatchException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final String error = ex.getValue() + " value for " + ex.getPropertyName() + " should be of type " + ex.getRequiredType(); final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } @Override protected ResponseEntity<Object> handleMissingServletRequestPart( final MissingServletRequestPartException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final String error = ex.getRequestPartName() + " part is missing" ; final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } @Override protected ResponseEntity<Object> handleMissingServletRequestParameter( final MissingServletRequestParameterException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final String error = ex.getParameterName() + " parameter is missing" ; final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } // @ExceptionHandler ({ MethodArgumentTypeMismatchException. class }) public ResponseEntity<Object> handleMethodArgumentTypeMismatch( final MethodArgumentTypeMismatchException ex, final WebRequest request) { logger.info(ex.getClass().getName()); // final String error = ex.getName() + " should be of type " + ex.getRequiredType().getName(); final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), error); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } @ExceptionHandler ({ ConstraintViolationException. class }) public ResponseEntity<Object> handleConstraintViolation( final ConstraintViolationException ex, final WebRequest request) { logger.info(ex.getClass().getName()); // final List<String> errors = new ArrayList<>(); for ( final ConstraintViolation<?> violation : ex.getConstraintViolations()) { errors.add(violation.getRootBeanClass().getName() + " " + violation.getPropertyPath() + ": " + violation.getMessage()); } final ApiError apiError = new ApiError(HttpStatus.BAD_REQUEST, ex.getLocalizedMessage(), errors); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } // 404 @Override protected ResponseEntity<Object> handleNoHandlerFoundException( final NoHandlerFoundException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final String error = "No handler found for " + ex.getHttpMethod() + " " + ex.getRequestURL(); final ApiError apiError = new ApiError(HttpStatus.NOT_FOUND, ex.getLocalizedMessage(), error); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } // 405 @Override protected ResponseEntity<Object> handleHttpRequestMethodNotSupported( final HttpRequestMethodNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final StringBuilder builder = new StringBuilder(); builder.append(ex.getMethod()); builder.append( " method is not supported for this request. Supported methods are " ); ex.getSupportedHttpMethods().forEach(t -> builder.append(t + " " )); final ApiError apiError = new ApiError(HttpStatus.METHOD_NOT_ALLOWED, ex.getLocalizedMessage(), builder.toString()); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } // 415 @Override protected ResponseEntity<Object> handleHttpMediaTypeNotSupported( final HttpMediaTypeNotSupportedException ex, final HttpHeaders headers, final HttpStatus status, final WebRequest request) { logger.info(ex.getClass().getName()); // final StringBuilder builder = new StringBuilder(); builder.append(ex.getContentType()); builder.append( " media type is not supported. Supported media types are " ); ex.getSupportedMediaTypes().forEach(t -> builder.append(t + " " )); final ApiError apiError = new ApiError(HttpStatus.UNSUPPORTED_MEDIA_TYPE, ex.getLocalizedMessage(), builder.substring( 0 , builder.length() - 2 )); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } // 500 @ExceptionHandler ({ Exception. class }) public ResponseEntity<Object> handleAll( final Exception ex, final WebRequest request) { logger.info(ex.getClass().getName()); logger.error( "error" , ex); // final ApiError apiError = new ApiError(HttpStatus.INTERNAL_SERVER_ERROR, ex.getLocalizedMessage(), "error occurred" ); return new ResponseEntity<>(apiError, new HttpHeaders(), apiError.getStatus()); } } |
4) create custom APiErro class to holds the errors
ApiError
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
| package com.test; import java.util.Arrays; import java.util.List; import org.springframework.http.HttpStatus; public class ApiError { private HttpStatus status; private String message; private List<String> errors; public ApiError() { super (); } public ApiError( final HttpStatus status, final String message, final List<String> errors) { super (); this .status = status; this .message = message; this .errors = errors; } public ApiError( final HttpStatus status, final String message, final String error) { super (); this .status = status; this .message = message; errors = Arrays.asList(error); } public HttpStatus getStatus() { return status; } public void setStatus( final HttpStatus status) { this .status = status; } public String getMessage() { return message; } public void setMessage( final String message) { this .message = message; } public List<String> getErrors() { return errors; } public void setErrors( final List<String> errors) { this .errors = errors; } public void setError( final String error) { errors = Arrays.asList(error); } } |
5) Add the below content to messages.properties and place in the resource folder.
message.properties
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
| name.not.empty=Please provide your name email.not.valid=Plese provide valid email address age.adult.only=must be less than or equal to {value} javax.validation.constraints.AssertFalse.message = must be false javax.validation.constraints.AssertTrue.message = must be true javax.validation.constraints.DecimalMax.message = must be less than ${inclusive == true ? 'or equal to ' : ''}{value} javax.validation.constraints.DecimalMin.message = must be greater than ${inclusive == true ? 'or equal to ' : ''}{value} javax.validation.constraints.Digits.message = numeric value out of bounds (<{integer} digits>.<{fraction} digits> expected) javax.validation.constraints.Email.message = must be a well-formed email address javax.validation.constraints.Future.message = must be a future date javax.validation.constraints.FutureOrPresent.message = must be a date in the present or in the future javax.validation.constraints.Max.message = must be less than or equal to {value} javax.validation.constraints.Min.message = must be greater than or equal to {value} javax.validation.constraints.Negative.message = must be less than 0 javax.validation.constraints.NegativeOrZero.message = must be less than or equal to 0 javax.validation.constraints.NotBlank.message = must not be blank javax.validation.constraints.NotEmpty.message = must not be empty javax.validation.constraints.NotNull.message = must not be null javax.validation.constraints.Null.message = must be null javax.validation.constraints.Past.message = must be a past date javax.validation.constraints.PastOrPresent.message = must be a date in the past or in the present javax.validation.constraints.Pattern.message = must match "{regexp}" javax.validation.constraints.Positive.message = must be greater than 0 javax.validation.constraints.PositiveOrZero.message = must be greater than or equal to 0 javax.validation.constraints.Size.message = size must be between {min} and {max} org.hibernate.validator.constraints.CreditCardNumber.message = invalid credit card number org.hibernate.validator.constraints.Currency.message = invalid currency (must be one of {value}) org.hibernate.validator.constraints.EAN.message = invalid {type} barcode org.hibernate.validator.constraints.Email.message = not a well-formed email address org.hibernate.validator.constraints.ISBN.message = invalid ISBN org.hibernate.validator.constraints.Length.message = length must be between {min} and {max} org.hibernate.validator.constraints.CodePointLength.message = length must be between {min} and {max} org.hibernate.validator.constraints.LuhnCheck.message = the check digit for ${validatedValue} is invalid, Luhn Modulo 10 checksum failed org.hibernate.validator.constraints.Mod10Check.message = the check digit for ${validatedValue} is invalid, Modulo 10 checksum failed org.hibernate.validator.constraints.Mod11Check.message = the check digit for ${validatedValue} is invalid, Modulo 11 checksum failed org.hibernate.validator.constraints.ModCheck.message = the check digit for ${validatedValue} is invalid, ${modType} checksum failed org.hibernate.validator.constraints.NotBlank.message = may not be empty org.hibernate.validator.constraints.NotEmpty.message = may not be empty org.hibernate.validator.constraints.ParametersScriptAssert.message = script expression "{script}" didn't evaluate to true org.hibernate.validator.constraints.Range.message = must be between {min} and {max} org.hibernate.validator.constraints.SafeHtml.message = may have unsafe html content org.hibernate.validator.constraints.ScriptAssert.message = script expression "{script}" didn't evaluate to true org.hibernate.validator.constraints.UniqueElements.message = must only contain unique elements org.hibernate.validator.constraints.URL.message = must be a valid URL org.hibernate.validator.constraints.br.CNPJ.message = invalid Brazilian corporate taxpayer registry number (CNPJ) org.hibernate.validator.constraints.br.CPF.message = invalid Brazilian individual taxpayer registry number (CPF) org.hibernate.validator.constraints.br.TituloEleitoral.message = invalid Brazilian Voter ID card number org.hibernate.validator.constraints.pl.REGON.message = invalid Polish Taxpayer Identification Number (REGON) org.hibernate.validator.constraints.pl.NIP.message = invalid VAT Identification Number (NIP) org.hibernate.validator.constraints.pl.PESEL.message = invalid Polish National Identification Number (PESEL) org.hibernate.validator.constraints.time.DurationMax.message = must be shorter than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} org.hibernate.validator.constraints.time.DurationMin.message = must be longer than${inclusive == true ? ' or equal to' : ''}${days == 0 ? '' : days == 1 ? ' 1 day' : ' ' += days += ' days'}${hours == 0 ? '' : hours == 1 ? ' 1 hour' : ' ' += hours += ' hours'}${minutes == 0 ? '' : minutes == 1 ? ' 1 minute' : ' ' += minutes += ' minutes'}${seconds == 0 ? '' : seconds == 1 ? ' 1 second' : ' ' += seconds += ' seconds'}${millis == 0 ? '' : millis == 1 ? ' 1 milli' : ' ' += millis += ' millis'}${nanos == 0 ? '' : nanos == 1 ? ' 1 nano' : ' ' += nanos += ' nanos'} |
6) Define Java class with validation annotation as below
POJO
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| package com.test; import java.io.Serializable; import javax.validation.constraints.Email; import javax.validation.constraints.Min; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.Size; public class Customer implements Serializable { @NotEmpty @Size (min = 2 , max = 14 ) private String name; @Email (message = "{email.not.valid}" ) private String email; @Min (value = 21 , message = "{age.adult.only}" ) private int age; //setters and getters } |
Note: {email.not.valid} should be present in message.properties.
7) Run the spring rest application and pass in valid input to the rest service
E.g:
{
"name" : "e", "age" : 1
}
}
Result:
{
"status": "BAD_REQUEST",
"message": "Validation Fail",
"errors": [
"age: must be less than or equal to 21",
"name: size must be between 2 and 14"
]
}
"status": "BAD_REQUEST",
"message": "Validation Fail",
"errors": [
"age: must be less than or equal to 21",
"name: size must be between 2 and 14"
]
}
references:
No comments:
Post a Comment