I am struggling with tailoring javax.validation.ConstraintValidator
and javax.validation.ConstraintValidatorContext
to my needs. The response message that I am receiving from a malformed request body always takes this shape:
<controller method name>.<input parameter>: <default message>, <controller method name>.<input parameter>: <specific validation message>
This message also is returned as a 500, rather than a 400 Bad Request. I have not been able to get a working to solution to do the following:
- Only include the
<specific validation message>
, i.e. exclude the method + input names - Exclude the default message
- Change 500 to 400 always, or potentially allow for custom Error responses
I have the following code:
Controller
import org.path.validation.ValidCreateThingRequest;
import org.path.service.ThingService;
// ... various other imports
@PostMapping("/things")
@ApiOperation(value = "Create a new thing")
@ApiResponse(code = 201, message = "Newly created thing", response = Thing.class)
@ResponseStatus(HttpStatus.CREATED)
public ThingResponseProto createThing(
@RequestBody @ValidCreateThingRequest final CreateThingRequestProto thingDto,
final HttpServletRequest httpServletRequest) {
final Context context = new RequestContext(httpServletRequest);
final Thing createdThing = thingService.createThing(thingDto);
return mapObjToProtoUtils.map(createdThing, ThingResponseProto.class);
}
Interface to create validation interface
@Constraint(validatedBy = CreateThingRequestValidator.class)
@Target({ METHOD,PARAMETER })
@Retention(RUNTIME)
@Documented
public @interface ValidCreateThingRequest {
String message() default "Request must be well-formed.";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
Validator used in interface
public class CreateCodeRequestValidator
implements ConstraintValidator<ValidCreateThingRequest, CreateThingRequestProto> {
private static final Pattern FORBIDDEN_NAME_CHARACTERS_REGEX =
Pattern.compile("[\\\\/\\t\\n\\r\\f?;]");
@Override
public void initialize(ValidCreateCodeRequest constraintAnnotation) {};
@Override
public boolean isValid(final CreateThingRequestProto thingDto, ConstraintValidatorContext context) {
return isValidCharacters(thingDto, context)
&& isValidNameExists(thingDto, context)
}
boolean isValidCharacters(final CreateThingRequestProto thingDto, ConstraintValidatorContext context) {
final String name = thingDto.getName();
if (FORBIDDEN_NAME_CHARACTERS_REGEX.matcher(name).find()) {
context
.buildConstraintViolationWithTemplate("Name must not contain forbidden characters.")
.addConstraintViolation();
return false;
} else {
return true;
}
}
boolean isValidNameExists(final CreateThingRequestProto thingDto, ConstraintValidatorContext context) {
final String name = thingDto.getName();
if (name != null && !name.trim().isEmpty()) {
context
.buildConstraintViolationWithTemplate("Name must not be null or empty.")
.addConstraintViolation();
return false;
} else {
return true;
}
}
}
Sending a malformed payload to the code above would result in a message that looks like this:
{
error: "Internal Server Error",
message: "createThing.thingDto: Request must be well-formed., createThing.thingDto: Name must not be null or empty.",
path: "/things"
status: 500
timestamp: 1607110364124
}
I would love to be able to receive this instead:
{
error: "Bad Request",
message: "Name must not be null or empty.",
path: "/things"
status: 400
timestamp: 1607110364124
}
Is this even possible based on buildConstraintViolationWithTemplate()
??