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.
Related
I have a rest service with my request body bean annotated with javax.validation like #NotBlank #NotNull #Pattern etc., and in one specific field I receive a file encoded as a string base64,
so, is there an annotation, or how could I write a custom validation annotation, so it would check if the string is really a base64 string?
I just need a validation like this in annotation form:
try {
Base64.getDecoder().decode(someString);
return true;
} catch(IllegalArgumentException iae) {
return false;
}
thnx in advance
Yes, you could write your own annotations and validators for them.
Your annotation would look like this:
#Documented
#Constraint(validatedBy = Base64Validator.class)
#Target( { ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface IsBase64 {
String message() default "The string is not base64 string";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Constraint validator javax.validation implementation (I'm using here your code for the actual validation):
public class Base64Validator implements ConstraintValidator<IsBase64, String> {
#Override
public boolean isValid(String value, ConstraintValidatorContext cxt) {
try {
Base64.getDecoder().decode(value);
return true;
} catch(IllegalArgumentException iae) {
return false;
}
}
}
Example data class with the annotated field:
#Data
public class MyPayload {
#IsBase64
private String value;
}
And controller method example with #Valid annotation which is required:
#PostMapping
public String test(#Valid #RequestBody MyPayload myPayload) {
return "ok";
}
I created a custom annotation
#Documented
#Constraint(validatedBy = CheckGranularityValidator.class)
#Target( { ElementType.PARAMETER} )
#Retention(RetentionPolicy.RUNTIME)
public #interface CheckGranularity {
String message() default "Duration has to be a multiple of granularity";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
With a validator like so
public class CheckGranularityValidator implements ConstraintValidator<CheckGranularity, AssetCostsRequest> {
#Override
public void initialize(final CheckGranularity constraintAnnotation) {
}
#Override
public boolean isValid(final AssetCostsRequest value, final ConstraintValidatorContext context) {
return value.getRange().getDuration() % value.getGranularity() == 0;
}
}
I tried using it in my RestController
#RestController
public class CalcApiController extends CalcApi {
#Override
public ResponseEntity<String> calcProfitability(#Valid #CheckGranularity #RequestBody final AssetCostsRequest assetCostsRequest) {
return ResponseEntity.ok("Works");
}
I tried using this annotation by writing a test:
#Test
public void calcTest() {
final AssetCostsRequest request = new AssetCostsRequest()
.setRange(new TimeRange(100L, 200L))
.setGranularity(26L);
given()
.contentType(ContentType.JSON)
.body(request)
.when()
.post("/calc")
.then()
.statusCode(HttpStatus.SC_BAD_REQUEST);
}
Relevant part of AssetCostsRequest:
public class AssetCostsRequest {
#JsonProperty
#NotNull
private TimeRange range;
#JsonProperty
#NotNull
private Long granularity = 30L;
...getters & setters
}
Test method returns with 200. When I try to set a breakpoint in isValid method, it isn't hit when I run the test. I tried changing order of annotations, getting rid of #Valid, changing #Target in CheckGranularity class, nothing helped. I'm using RestAssured for testing.
How do I make it, so my annotation is properly validating a parameter?
Change CheckGranularity's target to ElementType.TYPE and add #CheckGranularity directly on AssetCostsRequest. Also remove #CheckGranularity from endpoint definition.
How it works. By adding #Valid on endpoint's parameter you tell spring to validate it. Adding validation like #CheckGranularity won't work on the same level as Valid. It has to be added somewhere inside parameters class.
In Spring boot.
I want to do field validation and return an error if the input does not exist in the database.
I am trying to write the custom annotation for multiple input fields.
The controller is as below
#RestController
#Api(description = "The Mailer controller which provides send email functionality")
#Validated
public class SendMailController {
#Autowired
public SendMailService sendemailService;
org.slf4j.Logger logger = LoggerFactory.getLogger(SendMailService.class);
#RequestMapping(method = RequestMethod.POST, value = "/sendMail", consumes = {MediaType.TEXT_XML_VALUE, MediaType.APPLICATION_JSON_VALUE}, produces = {"text/xml", "application/json"})
#ResponseBody
#Async(value = "threadPoolTaskExecutor")
#ApiOperation("The main service operation which sends one mail to one or may recipient as per the configurations in the request body")
public Future<SendMailResult> sendMail(#ApiParam("Contains the mail content and configurations to be used for sending mail") #Valid #RequestBody MailMessage message) throws InterruptedException {
SendMailResult results = new SendMailResult();
try {
sendemailService.sendMessages(message);
long txnid = sendemailService.createAudit (message);
results.setTxnid (txnid);
results.setStatus("SUCCESS");
} catch(MessagingException | EmailServiceException e) {
logger.error("Exception while processing sendMail " + e);
results.setStatus("FAILED");
// TODO Handle error create results
e.printStackTrace();
} catch(Exception e) {
logger.error("Something went wrong " + e);
results.setStatus("FAILED");
// TODO Handle error create results
e.printStackTrace();
}
return new AsyncResult<SendMailResult>(results);
}
}
one DTO that is mapped with request
public class MailContext {
#NotNull
private String clientId;
#NotNull
private String consumer;
public int getClientId() {
return Integer.parseInt(clientId);
}
public void setClientId(String clientId) {
this.clientId = clientId;
}
public String toJson() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
String writeValueAsString = mapper.writeValueAsString(this);
return writeValueAsString;
}
}
Request xml
<mailMessage>
<mailContext>
<clientId>10018</clientId>
<consumer>1</consumer>
</mailContext>
</mailMessage>
I want to write a custom annotation to validate client which exists in the database (table client_tbl) if provided in the request.
consumer: is present in database table cunsumer_tbl
if these not present in database send error message else call service method.
Please suggest how to write such custom annotation with the error.
I know another way to validate this.
Inside your controller, you can register a validator.
#InitBinder
public void setup(WebDataBinder webDataBinder) {
webDataBinder.addValidators(dtoValidator);
}
Where dtoValidator is an instance of Spring Bean, for example, which must implements org.springframework.validation.Validator.
So, you just have to implement two methods: supports() and validate(Object target, Errors errors);
Inside supports() method you can do whatever you want to decide whether the object should be validated by this validator or not. (for example, you can create an interface WithClientIdDto and if the tested object isAssignableFrom() this interface you can do this validation. Or you can check your custom annotation is presented on any field using reflection)
For example: (AuthDtoValidator.class)
#Override
public boolean supports(Class<?> clazz) {
return AuthDto.class.isAssignableFrom(clazz);
}
#Override
public void validate(Object target, Errors errors) {
final AuthDto dto = (AuthDto) target;
final String phone = dto.getPhone();
if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(dto.getEmail())) {
errors.rejectValue("email", "", "The phone or the email should be defined!");
errors.rejectValue("phone", "", "The phone or the email should be defined!");
}
if (!StringUtils.isEmpty(phone)) {
validatePhone(errors, phone);
}
}
UPDATE:
You can do that.
Create an annotation
for example:
#Target({ FIELD })
#Retention(RUNTIME)
#Constraint(validatedBy = ClientIdValidator.class)
#Documented
public #interface ClientId {
String message() default "{some msg}";
Class<?>[] groups() default { };
Class<? extends Payload>[] payload() default { };
}
and implement this validator:
class ClientIdValidator implements ConstraintValidator<ClientId, Long> {
#Override
public boolean isValid(Long value, ConstraintValidatorContext context) {
//validation logc
}
}
More details you can find here: https://reflectoring.io/bean-validation-with-spring-boot/
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() { }
I am trying to use spring to check user online input to ensure that the two characters they enter is an actual US state, is there any way of doing this, hopefully using a preset pattern? like, #State or something (if that was a legit annotation). Also, is there a good annotation commonly used for a String street, and String city field? That is other than #NotNull and #NotEmpty
Any help would be greatly appreciated!!
Unfortunately there is no out of the box however you can create your own #State annotation , all you need is to define your annotation and class implementing ConstraintValidator(which handles the validation logic) E.g.
#Constraint(validatedBy = StateConstraintValidator.class)
#Target( { ElementType.FIELD })
#Retention(RetentionPolicy.RUNTIME)
public #interface State {
String message() default "{State}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
public class StateConstraintValidator implements ConstraintValidator<String, String> {
private static final Set<String> CODE_MAP = new HashSet<>(){
{add("AR");}
{add("AK");} //add more codes ...
};
#Override
public void initialize(String state) { }
#Override
public boolean isValid(String value, ConstraintValidatorContext cxt) {
if(value == null) {
return false;
}
return CODE_MAP.contains(value);
}
}
In the similar manner you can create other annotations.