How to validate Integer or long in spring mvc - java

class employee{
...
private long phone;
...
}
I want to validate phone number using spring jsr303 validator, In my Controller I am using #valid. I am successfully validating entered value is number or string by using generic typeMismatch placing in error message property file.
But I want to validate entered number format is correct or not.(#pattern for string only)
How to achieve this one,please suggest me.

Normally phone numbers are String and you can validate by using #Pattern, but if you want to validate any fields you can do like this.
Custom annotation Javax validator
#javax.validation.Constraint(validatedBy = { PhoneNumberConstraintValidator.class })
#Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })
#Retention(RUNTIME)
public #interface ValidPhoneNumber {
}
public class PhoneNumberConstraintValidator implements ConstraintValidator<ValidPhoneNumber, Long> {
#Override
public void initialize(final ValidPhoneNumber constraintAnnotation) {
// nop
}
#Override
public boolean isValid(final Long value, final ConstraintValidatorContext context) {
//your custom validation logic
}
}
class employee{
...
private long phone;
#ValidPhoneNumber
public Long getPhone() { return phone; }
...
}
OR simpler if you have hibernate validator, you can just add this method in your entity class.
#org.hibernate.validator.AssertTrue
public boolean validatePhoneNumber() { }

Related

Convert List of Enums to List of String for Spring #RequestParam using feign client

I have an enum class as such:
ONE("1", "Description1"),
TWO("2", "Description2");
String value;
String description;
MyEnum(String value, String description) {
this.value = value;
this.description = description;
}
#Override
public String toString() {
return this.value;
}
#JsonValue
public String value() {
return this.value;
}
The API I am interacting with is expecting a param with type String and the values can be comma separated.
For example: api.com/test?param1=1,2
I configured a feign client with the url api.com/test
And then created a POJO like so
public class POJO {
private List<MyEnum> param1;
}
And in my feign client I have:
#RequestMapping(method = RequestMethod.GET)
MyResponse getResponse(#SpringQueryMap POJO request);
Is it possible to somehow turn the List of Enums to a List of String before the API call is made via some Spring approach?
As of right now, when I pass a List of Enums, it is only taking into account the last Enum within this list.
UPDATE: I annotated the property I want to convert to a list using #JsonSerialize(converter=abc.class). However #SpringQueryMap doesn't seem to honor that serialization..
Yes is possible, you need to create an interceptor and in that method do the mapping.
This topic may be for you.
Spring - Execute code before controller's method is invoked
So turns out #JsonSerialize was not working with #SpringQueryMap
So I did have to add an interceptor.
Like so:
public class MyInterceptor implements RequestInterceptor {
#Override
public void apply(RequestTemplate requestTemplate) {
if(requestTemplate.queries().containsKey("param1")) {
requestTemplate.query("param1", convert(requestTemplate.queries().get("param1")));
}
}
//convert list to a string
public String convert(Collection<String> values) {
final String s = String.join(",", values.stream().map(Object::toString).collect(Collectors.toList()));
return s;
}
}
And then in my Feign config class added this:
#Bean
public MyInterceptor myInterceptor() {
return new MyInterceptor();
}

Is there a declarative way to validate request parameters depending from one of request fields?

I'm looking for something like JSR-303 Validation Groups (bean validation, when you mark method argument with #Validated(GroupName.class) in controller and specify group in request class fields where needed), but it should decide how to validate at runtime depending on one of request fields.
For example, if we have controller class like this
#Controller
public class MyController {
#Autowired
private MyService myService;
#ResponseBody
#RequestMapping(value = "/path", method = RequestMethod.PUT)
public ResponseVo storeDetail(/*maybe some annotation here*/ DetailRequestVo requestVo) {
return myService.storeDetail(requestVo);
}
class DetailRequestVo {
String type;
Long weight;
Long radius;
}
}
And we want validation depending on type field value: if type = "wheel" then radius and weight fields should be presented, if type = "engine" then only weight field should be presented.
Does Spring (as of 3.2.17) provide API to implement these rules in more declarative approach? org.springframework.validation.Validator looks like not an option here, because its method boolean supports(Class<?> clazz) decides based on class info, not instance info.
Thanks in advance
Not sure about Spring but you can do that with plain JSR-303 using a custom Validator for the class itself... like
#Retention(RetentionPolicy.RUNTIME)
#Target({ ElementType.TYPE })
#Constraint(validatedBy = TypeValidator.class)
#Documented
public #interface ValidType {
...
}
public class TypeValidator implements ConstraintValidator<ValidType , Object> {
public boolean isValid(final Object target, final ConstraintValidatorContext context) {
DetailRequestVo request = (DetailRequestVo) target;
// do your checks here
}
and used like
#ValidType
class DetailRequestVo {
String type;
Long weight;
Long radius;
}
Since the custom Validator has access to the whole DetailRequestVo-Object you can do your check of field A depending on field B etc.

JSR-303 validation groups define a default group

I have a bean that has a lot of fields annotated with JSR-303 validation annotations. There is a new requirement now that one of the fields is mandatory, but only in certain conditions.
I looked around and have found what I needed, validation groups.
This is what I have now:
public interface ValidatedOnCreationOnly {
}
#NotNull(groups = ValidatedOnCreationOnly.class)
private String employerId;
#Length(max = 255)
#NotNull
private String firstName;
#Length(max = 255)
#NotNull
private String lastName;
However, when I run this validation in a unit test:
#Test
public void testEmployerIdCanOnlyBeSetWhenCreating() {
EmployeeDTO dto = new EmployeeDTO();
ValidatorFactory vf = Validation.buildDefaultValidatorFactory();
Set<ConstraintViolation<EmployeeDTO>> violations = vf.getValidator().validate(dto, EmployeeDTO.ValidatedOnCreationOnly.class);
assertEquals(violations.size(), 3);
}
It turns out that all of the non-group annotated validations are ignored and I get only 1 violation.
I can understand this behaviour but I would like to know if there is a way I can make the group include all non-annotated parameters as well. If not I'd have to do something like this:
public interface AlwaysValidated {
}
public interface ValidatedOnCreationOnly extends AlwaysValidated {
}
#NotNull(groups = ValidatedOnCreationOnly.class)
private String employerId;
#Length(max = 255, groups = AlwaysValidated.class)
#NotNull(groups = AlwaysValidated.class)
private String firstName;
#Length(max = 255, groups = AlwaysValidated.class)
#NotNull(groups = AlwaysValidated.class)
private String lastName;
The real class I'm working with has a lot more fields (about 20), so this method turns what was a clear way of indicating the validations into a big mess.
Can anyone tell me if there is a better way? Maybe something like:
vf.getValidator().validate(dto, EmployeeDTO.ValidatedOnCreationOnly.class, NonGroupSpecific.class);
I'm using this in a spring project so if spring has another way I'll be glad to know.
There is a Default group in javax.validation.groups.Default, which represents the default Bean Validation group. Unless a list of groups is explicitly defined:
constraints belong to the Default group
validation applies to the Default group
You could extends this group:
public interface ValidatedOnCreationOnly extends Default {}
just wanted to add more:
if you're using spring framework you can use org.springframework.validation.Validator
#Autowired
private Validator validator;
and to perform validation manually:
validator.validate(myObject, ValidationErrorsToException.getInstance());
and in controller:
#RequestMapping(method = RequestMethod.POST)
public Callable<ResultObject> post(#RequestBody #Validated(MyObject.CustomGroup.class) MyObject request) {
// logic
}
although in this way extending from javax.validation.groups.Default won't work so you have to include Default.class in groups:
class MyObject {
#NotNull(groups = {Default.class, CustomGroup.class})
private String id;
public interface CustomGroup extends Default {}
}
For me add Default.class everywhere is not good approach.
So I extended LocalValidatorFactoryBean which validate with some group and delegate for validation without any group.
I used spring boot 2.2.6.RELEASE
and I used spring-boot-starter-validation dependency.
My bean for validattion
public class SomeBean {
#NotNull(groups = {UpdateContext.class})
Long id;
#NotNull
String name;
#NotNull
String surName;
String optional;
#NotNull(groups = {CreateContext.class})
String pesel;
#Valid SomeBean someBean;
}
code of own class which extends LocalValidatorFactoryBean
public class CustomValidatorFactoryBean extends LocalValidatorFactoryBean {
#Override
public void validate(Object target, Errors errors, Object... validationHints) {
if (validationHints.length > 0) {
super.validate(target, errors, validationHints);
}
super.validate(target, errors);
}
}
Put it to spring context via #Bean or just with #Component (as you wish)
#Bean
#Primary
public LocalValidatorFactoryBean customLocalValidatorFactoryBean() {
return new CustomValidatorFactoryBean();
}
usage of it in some RestController
// So in this method will do walidation on validators with CreateContext group and without group
#PostMapping("/create")
void create(#RequestBody #Validated(CreateContext.class) SomeBean someBean) {
}
#PostMapping("/update")
void update(#RequestBody #Validated(UpdateContext.class) SomeBean someBean) {
}
Due to some reason testValidation is not working when is invoked DummyService.testValidation() by RestController or other spring bean.
Only on RestController side is working :/
#Validated
#Service
#AllArgsConstructor
class DummyService {
public void testValidation(#NotNull String string, #Validated(UpdateContext.class) SomeBean someBean) {
System.out.println(string);
System.out.println(someBean);
}
}

How to validate XML input not-null property in Java REST service?

I am running the service under TomEE.
The model is very simple:
#XmlRootElement
#XmlAccessorType(XmlAccessType.FIELD)
public class Card {
#XmlElement(required = true, nillable = false)
private String cardNumber;
public Card() {
//no-op
}
public Card(final String s) {
cardNumber = s;
}
public String getCardNumber() {
return cardNumber;
}
public void setCardNumber(String cardNumber) {
this.cardNumber = cardNumber;
}
}
I followed this example
https://docs.oracle.com/javaee/7/tutorial/jaxrs-advanced008.htm
The service is also very simple like:
#Consumes(APPLICATION_XML)
#Produces(APPLICATION_XML)
public class MyService {
#POST
#Path("status")
public String queryStatus(Card card) {
// do something
}
}
If my input is wrongly formatted, it will have a proper exception. But it doesn't seem to be able to validate empty card number or null.
For example, when I have
"<card></card>"
or
"<card><cardNumber> </cardNumber></card>"
(with an empty string), the service still goes through, with the "cardNumber" property being null or empty.
Well, I could do something in the setter to throw out an exception. But I was hoping JavaEE automatically handle this kind of this if I put the annotation on the property.
So what am I missing here?
Thank you for any tips!
With Bean Validation (http://beanvalidation.org/) Java EE offers a standard way to validate objects. It is also integrated with JAX RS.
So you can use annotations like #NotNull in your Card class. In your Service just say that you want a #Valid Card.
An example can be found here: https://jaxenter.com/integrating-bean-validation-with-jax-rs-2-106887.html

How do I use Custom Validations in Jersey

I want to Implement a validation in a jersey such that if I send a duplicate value of UserName or Email which already exists in DataBase then it should throw an Error saying UserName/Email already exists.
How can I acheive this?
I gone through this jersey documentation
https://jersey.java.net/documentation/latest/bean-validation.html
https://github.com/jersey/jersey/tree/2.6/examples/bean-validation-webapp/src
But I couldn't understood what exactly I have to follow to make my custom Jersey validations.
Suppose I send a Json in Body while Creating a User like:
{
"name":"Krdd",
"userName":"khnfknf",
"password":"sfastet",
"email":"xyz#gmail.com",
"createdBy":"xyz",
"modifiedBy":"xyz",
"createdAt":"",
"modifiedAt":"",
}
Thanks in Advance for your helping hands.
Assuming you have a request instance of class:
public class UserRequest {
// --> NOTICE THE ANNOTATION HERE <--
#UniqueEmail(message = "email already registered")
private final String email;
public UserRequest(String email) {
this.email = email;
}
public String getEmail() {
return email;
}
}
You have to add a new annotation (and link it to your validator class using #Constraint):
#Target({ ElementType.FIELD, ElementType.ANNOTATION_TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Constraint(validatedBy = { UniqueEmailValidator.class })
#Documented
public #interface UniqueEmail {
String message();
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
then you also have to implement the validation itself:
public class UniqueEmailValidator implements ConstraintValidator<UniqueEmail, UserRequest> {
#Override
public void initialize(UniqueEmail constraintAnnotation) {
}
#Override
public boolean isValid(UserRequest value, ConstraintValidatorContext context) {
// call to the DB and verify that value.getEmail() is unique
return false;
}
}
and you're done. Remember that Jersey is using HK2 internally so binding some sort of a DAO to your Validator instance can be tricky if you use Spring or other DI.

Categories

Resources