I am using Spring for a web application. To validate a user's input in a form such as for creating a Person entity I use JSR 303 validation to check for not null/empty or valid patterns etc.. Some things however I check in the service layer such as making sure a field is unique in the database and if not throwing an exception. I catch this exception in the controller and add an error to the field.
This is basically what Ben J is doing in Option 1 here https://stackoverflow.com/questions/3224749/passing-errors-back-to-the-view-from-the-service-layer
Service Layer
public Person createPerson(String username, String fullName, String specialCode)
throws DuplicateUsernameException {
// Check if username exists and throw exception otherwise create person.
if (checkUsernameExists(username)) {
throw new DuplicateUsernameException();
}
Person person = new Person(username, fullName, specialCode);
return personDao.create(person);
}
Controller
void createPerson(Form form, Errors errors) {
try {
service.createPerson(form.getUsername(), form.getFullName(),
form.getSpecialCode());
} catch (DuplicateUsernameException e) {
errors.add("username", "This username exists");
}
// render view
...
}
Up until now this has served me well but I now have a requirement to check two fields are individually unique and want to inform the user. In the above example if I wanted to check the specialCode was also unique I could add another check below the existing one in the service layer and throw a separate DuplicateSpecialCodeException.
Service Layer
public Person createPerson(String username, String fullName, String specialCode)
throws DuplicateUsernameException, DuplicateSpecialCodeException {
if (checkUsernameExists(username)) {
throw new DuplicateUsernameException();
}
if (checkSpecialCodeExists(specialCode)) {
throw new DuplicateSpecialCodeException();
}
Person person = new Person(username, fullName, specialCode);
return personDao.create(person);
}
Controller
void createPerson(Form form, Errors errors) {
try {
service.createPerson(form.getUsername(), form.getFullName(),
form.getSpecialCode());
} catch (DuplicateUsernameException e) {
errors.add("username", "This username exists");
} catch (DuplicateSpecialCodeException e) {
errors.add("specialCode", "This special code exists");
}
// render view
...
}
The problem is if the user submits a duplicate Username and a duplicate SpecialCode they will only be informed of the duplicate Username. I want to shown both fields.
What is a neat way of doing this?
My ideas so far are:
- Throw an exception that contains all the errors.
- Pass the Errors object to the service and add errors to this.
Both of these methods would rely on the service layer knowing about the name of the fields and a suitable error code which seems to go against keeping the layers separate.