Praveen D
Praveen D

Reputation: 2377

How to write custom validation in rest api?

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>
  1. I want to write a custom annotation to validate client which exists in the database (table client_tbl) if provided in the request.
  2. 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.

Upvotes: 0

Views: 5361

Answers (1)

Vladyslav Diachenko
Vladyslav Diachenko

Reputation: 785

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/

Upvotes: 1

Related Questions