How can I add custom validation to a file upload with OpenAPI 3 in SpringBoot?

Viewed 398

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?

0 Answers
Related