Skip to content

Instantly share code, notes, and snippets.

@susimsek
Last active September 13, 2023 02:04
Show Gist options
  • Save susimsek/03b6a4d695b864dfe95d1b31959b3e53 to your computer and use it in GitHub Desktop.
Save susimsek/03b6a4d695b864dfe95d1b31959b3e53 to your computer and use it in GitHub Desktop.
Spring Boot File Content Type Validation With Annotation
@ControllerAdvice
public class CustomExceptionControllerAdvice {
@ExceptionHandler(MultipartException.class)
void handleMultipartException(MultipartException ex,HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value(),"Please select a file");
}
@ExceptionHandler(ConstraintViolationException.class)
public void handleConstraintViolationException(ConstraintViolationException ex,HttpServletResponse response) throws IOException {
response.sendError(HttpStatus.BAD_REQUEST.value());
}
}
@RestController
@FieldDefaults(level = AccessLevel.PRIVATE,makeFinal=true)
@RequestMapping("/versions/1")
public class FileController {
@PostMapping("/files")
@ResponseStatus(HttpStatus.OK)
public String createFile(@Validated @ValidFile @RequestPart("file") MultipartFile file {
return file.getOriginalFilename()
}
}
public class FileValidator implements ConstraintValidator<ValidFile, MultipartFile> {
@Override
public void initialize(ValidFile constraintAnnotation) {
}
@Override
public boolean isValid(MultipartFile multipartFile, ConstraintValidatorContext context) {
boolean result = true;
String contentType = multipartFile.getContentType();
if (!isSupportedContentType(contentType)) {
result = false;
}
return result;
}
private boolean isSupportedContentType(String contentType) {
return contentType.equals("text/xml")
|| contentType.equals("application/pdf")
|| contentType.equals("image/png")
|| contentType.equals("image/jpg")
|| contentType.equals("image/jpeg");
}
}
@Documented
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = {FileValidator.class})
public @interface ValidFile {
String message() default "Only PDF,XML,PNG or JPG images are allowed";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
@vikassl
Copy link

vikassl commented Jul 3, 2022

I tried to do something similar to this! It is working great. I'm trying to return a message from constraint violation exception and it's returning me the message but it's returning the propertyPath of the violation as well.
RESPONSE: uploadCafeImage.file: Max allowed file size is 200KB only. Uploaded file size 675KB
EXPECTED: Max allowed file size is 200KB only. Uploaded file size 675KB

Can you suggest a way to get rid of this uploadCafeImage.file property path from the response?

@yildizmy
Copy link

For validation exceptions e.g. ConstraintViolationException, I use another exception handler. You may have a look at validator package in the following repo:

yildizmy/upload-csv-into-mysql-using-apache-commons-csv

@SharkFourSix
Copy link

This can still be bypassed because you're only relying on the headers, which can be spoofed. True content validation can only happen when you inspect the uploaded file (the temporary file) before accepting it and moving it to your destination

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment