3

I'm working with mybatis to perform all database actions. I'm also working with an Angular front-end, so the validations in the client are made with angular-validation-ghiscoding and the native HTML5 validations. I would like to validate the data in the bank-end to, but I don't want to use annotations.

Here is an example of the code:

    @RequestMapping(value = SecureApiResources.URI_UPDATE_ACCOUNT, method = RequestMethod.POST, produces = "application/json")
    public @ResponseBody Account updateAccount(
            @RequestBody final AccountRequestUpdate accountRequest) { // Object to be validated (accountRequest)

        Account account = accountMapper.getAccountOfMerchant(authContextHolder.getMerchantId(), authContextHolder.getDefaultAccountId());

        if (account == null) {
            HttpErrors httpErrors = new HttpErrors(
                    SecureApiResources.ERROR_ACCOUNTS_NOT_FOUND);
            throw new EntityNotFoundException(httpErrors);
        }
        int resultUpdate;
        try {
            // In this point I should validate the accountRequest object
            account.setAccountName(accountRequest.getAccountName());
            account.setCommercialName(accountRequest.getCommerciaName());
            account.setCountry(accountRequest.getCountry());
            account.setCity(accountRequest.getCity());
            resultUpdate = accountMapper.updateMerchant(account);
            if (resultUpdate == 0) {
                HttpErrors httpErrors = new HttpErrors(
                        SecureApiResources.ERROR_ACCOUNTS_NOT_FOUND);
                throw new EntityNotFoundException(httpErrors);
            }
        } catch (Exception e) {
            HttpErrors httpErrors = new HttpErrors(
                    SecureApiResources.ERROR_SQL_NOT_EXECUTE);
            throw new EntityNotFoundException(httpErrors);
        }

        return account;
    }

In the same class I have a method to create an account and I reciebe another model object (AccountRequestCreate accountRequest).

Which could be the most recommended option to implement without xml neither annotations?

1
  • Ironically, the most recommend approach is using @Valid or @validated annotations! What's the problem of annotations? You've already added a @RequestBody, i guess you can make room for one more annotation. Commented Mar 4, 2016 at 19:38

1 Answer 1

5

The most recommended approach is using @Valid or @Validated annotations but since you're not totally OK with that, you can Autowire the javax.validation.Validator into your controller and perform the validation manually:

@Controller
public class SomeController {
    @Autowired private Validator validator;

    @RequestMapping(...)
    public ResponseEntity<?> someHandler(@RequestBody SomeBody body) {
        Set<ConstraintViolation<SomeBody>> violations = validator.validate(body);
        if (!violations.isEmpty()) {
            List<String> messages = violations
                                   .stream()
                                   .map(ConstraintViolation::getMessage)
                                   .collect(Collectors.toList());

            return ResponseEntity.badRequest().body(messages);
        }
        // the rest of controller
    }
}

With this approach, you will repeat the validation logic in all other controllers which is, well, not a good idea. Also you're violating the DRY principle.

As i said it's better to annotate your bean with @Valid or @Validated:

@RequestMapping(...)
public ResponseEntity<?> someHandler(@RequestBody @Valid SomeBody body) { ... }

If passed bean violates at least one validation rule, an MethodArgumentNotValidException would be thrown. For handling that exception you can write a ControllerAdvice that catches the exception and return a suitable HTTP response, say a 400 Bad Request:

@ControllerAdvice
public class ValidationAdvice {
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<?> handleValidationError(MethodArgumentNotValidException ex) {
         List<String> validationErrors = ex.getBindingResult()
                                            .getAllErrors()
                                            .stream()
                                            .map(ObjectError::getDefaultMessage)
                                            .collect(Collectors.toList());

         return ResponseEntity.badRequest().body(validatioErrors);
    }
}
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.