Thomas Stubbe
Thomas Stubbe

Reputation: 2014

Jackson deserialization errorhandling in spring-framework

I'm looking for a clean way to handle Jackson Deserialization errors for REST web requests.

More precisely: I have an Enum in a incoming DTO object, mapped from JSON. But if the user sends a wrong value, a 400 Bad Request is returned. I would like to return a 422 Unprocessable Entity with a correct message.

One option would be to accept a String, and use bean validation. However, it's not possible to pass all enum values as a list to the annotation (not a constant), so I would need to pass all enum values separately and keep them up to date. This will be very error prone over the whole application. I'm looking for a more structural way to handle this.

Upvotes: 1

Views: 873

Answers (1)

Thomas Stubbe
Thomas Stubbe

Reputation: 2014

I solved this by using a String in the DTO and using a public @interface EnumValueas annotation.

The EnumValue:

@ReportAsSingleViolation
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = EnumValueValidator.class)
@Target(ElementType.FIELD)
public @interface EnumValue {
    Class<? extends Enum> value();
    String message() default "The input contains validation errors.";
    Class<?>[] groups() default { };
    Class<? extends Payload>[] payload() default { };
}

The validator:

public class EnumValueValidator implements ConstraintValidator<EnumValue, String> {

    private Class<? extends Enum> enumClass;
    private String message;

    @Override
    public void initialize(final EnumValue constraintAnnotation) {
        this.enumClass = constraintAnnotation.value();
        this.message = constraintAnnotation.message();
    }

    @Override
    public boolean isValid(final String value, final ConstraintValidatorContext context) {
        boolean valid = false;
        for (final Enum enumValue : enumClass.getEnumConstants()) {
            if (enumValue.name().equals(value)) {
                valid = true;
            }
        }
        if (!valid) {
            context.buildConstraintViolationWithTemplate(message) //
                   .addConstraintViolation();
        }
        return valid;
    }
}

Upvotes: 3

Related Questions