I want to add custom validation to a file upload in Spring using OpenAPI3. The file will be an excel file that should be validated that it is of:
- type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
- size: smaller than 20MB
This is the endpoint in the api.yaml:
/documentUpload:
post:
tags:
- My Excel Upload
summary: Excel document upload
description: Upload a document that is an excel file
operationId: uploadDocument
requestBody:
content:
multipart/form-data:
schema:
$ref: '#/components/schemas/DocumentUploadRequest'
responses:
'202':
description: Document recieved.
and the request object:
DocumentUploadRequest:
type: object
description: Upload a document together with metadata for it
required:
- type
- file
properties:
type:
$ref: '#/components/schemas/DocumentType'
file:
type: string
format: binary
description: Document in binary format
and here the controller method from where I want to override the generated API method:
@Override
public ResponseEntity<Void> uploadDocument(
DocumentType type,
MultipartFile file) {
// Validation required
return ResponseEntity.ok().build();
}
Since I cannot use @Valid on the file parameter, because it is already defined in the super class (on all parameters by default), I tried to write a custom Validator (using org.springframework.validation.Validator) like this:
@Component
public class MyFileValidator implements Validator {
private static final int MAX_FILE_SIZE = 20000000;
private static final String ALLOWED_FILE_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
@Override
public boolean supports(Class<?> aClass) {
if (aClass.isInstance(MassChangeDocumentType.class)) {
return true;
}
return MultipartFile.class.isAssignableFrom(aClass);
}
@Override
public void validate(Object target, Errors errors) {
ValidationUtils.rejectIfEmpty(errors, "file", "file.empty");
MultipartFile file = (MultipartFile) target;
rejectIfFileTypeInvalid(errors, file);
rejectIfFileIsTooLarge(errors, file);
}
...
And added this Validation to the Controller class using the @InitBinder annotation:
@RestController
@RequiredArgsConstructor
public class DocumentUploadController implements DocumentUploadApi {
private final MyFileValidator fileValidator;
@InitBinder
protected void initBinder(WebDataBinder binder) {
if (binder.getTarget() == null) {
return;
}
if (binder.getTarget().getClass().isAssignableFrom(MultipartFile.class)) {
binder.addValidators(fileValidator);
}
}
@Override
public ResponseEntity<Void> uploadDocument(
DocumentType type,
MultipartFile file) {
// Validation required
return ResponseEntity.ok().build();
}
...
The result with this is that only the type argument is being evaluated in the initBinder method. The file argument is being ignored and not validated at all. Am I even using the correct approach for this kind of custom validation?