Hello everybody,
Greetings of the day!
Today we will explore how to validate file types in Spring Boot Rest API using custom annotations. Therefore, we create a custom annotation that can be used when file types need to be validated throughout the application.
Before you start, if you have questions about uploading JSON and multipart files in a single REST API call, check out the link below.
Spring Boot REST API File Upload With JSON Data In Single API Call
So let's begin.
First, create a basic project structure for your project by visiting the Spring Initializr and adding web, validation, and Lombok dependencies.
Now unzip your project and import it into an IDE.
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.11.0</version> </dependency>
package com.learning.file.mgt.enums; public enum FILE_TYPE { PDF, PNG; }
Next, let's create a custom @FileType annotation that can take two input values ​​AllowedFileTypes, an array of FILE_TYPE, and a message.
Example
@FileType(allowedFileTypes = {FILE_TYPE.PDF},message = "Please enter valid file format")
We also need to create a FileTypeValidator class that will implement ConstraintValidator in which we will add code to validate the file extension.
Here is the code for the creation of the FileType annotation & FileTypeValidator.
FileType.java
package com.learning.file.mgt.validation; import com.learning.file.mgt.enums.FILE_TYPE; import javax.validation.Constraint; import javax.validation.Payload; import java.lang.annotation.*; @Target({ElementType.FIELD,ElementType.METHOD ,ElementType.ANNOTATION_TYPE}) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = FileTypeValidator.class) @Documented public @interface FileType { Class<? extends Payload> [] payload() default{}; Class<?>[] groups() default {}; String message() default "Invalid file type."; FILE_TYPE[] allowedFileTypes(); }
FileTypeValidator.java
package com.learning.file.mgt.validation; import org.apache.commons.io.FilenameUtils; import org.springframework.web.multipart.MultipartFile; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; import java.util.ArrayList; import java.util.Arrays; import java.util.List; public class FileTypeValidator implements ConstraintValidator<FileType, MultipartFile> { private List<String> validFileTypes; @Override public void initialize(FileType fileType) { validFileTypes=new ArrayList<>(); Arrays.stream(fileType.allowedFileTypes()) .forEach(file_type -> validFileTypes .add(file_type.name())); } @Override public boolean isValid(MultipartFile multipartFile, ConstraintValidatorContext constraintValidatorContext){ String extension = FilenameUtils.getExtension (multipartFile.getOriginalFilename()) .toUpperCase(); return validFileTypes.contains(extension); } }
Next, let's create ResponseDTO.java to create and send API responses.
package com.learning.file.mgt.dto; import lombok.*; @Getter @Setter @AllArgsConstructor @NoArgsConstructor @Builder public class ResponseDTO<T> { private String message; private T response; }
Every time we get an invalid file in request @FileType annotation will throw BindException so we need to handle and display the appropriate response to the end user.
For that let's create GlobalExceptionHandler.java using @ControllerAdvice.
package com.learning.file.mgt.controlleradvice; import com.learning.file.mgt.dto.ResponseDTO; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.validation.BindException; import org.springframework.validation.FieldError; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import java.util.HashMap; import java.util.Map; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(BindException.class) public ResponseEntity<ResponseDTO<Object>> handleBindException (BindException bindException){ Map<String,String> errorMap=new HashMap<>(); bindException.getAllErrors().stream() .forEach(objectError -> { errorMap.put(((FieldError)objectError).getField() ,objectError.getDefaultMessage()); }); return new ResponseEntity( ResponseDTO.builder(). response(errorMap.toString()) .message("Object validation failed") .build(), HttpStatus.BAD_REQUEST); } }
Now we will create FileUploadDTO.java which will be used as Request Body. Here on MultipartFile, we specify allowedFileTypes as pdf under @FileType annotation.
package com.learning.file.mgt.dto; import com.learning.file.mgt.enums.FILE_TYPE; import com.learning.file.mgt.validation.FileType; import lombok.AllArgsConstructor; import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; import org.springframework.web.multipart.MultipartFile; import javax.validation.constraints.NotBlank; @Getter @Setter @AllArgsConstructor @NoArgsConstructor public class FileUploadDTO { @NotBlank private String fileDescription; @FileType(allowedFileTypes = {FILE_TYPE.PDF}, message = "Please enter valid file format") private MultipartFile file; }
Now we will create a REST API Post endpoint just to check whether our validations are working as expected or not, here we are not adding any business logic.
In order to trigger validation, we need to use @Valid annotation with @RequestBody or @ModelAttribute as shown below.
package com.learning.file.mgt.controller; import com.learning.file.mgt.dto.FileUploadDTO; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.validation.Valid; @RestController @RequestMapping("manage-file") public class FileMgtController { @PostMapping public void addFile(@Valid @ModelAttribute FileUploadDTO fileUploadDTO){ System.out.println("File is valid"); } }
Here is the result if we upload an invalid file.
{
"message": "Object validation failed",
"response": "{file=Invalid file type.}"
}
Enclose the project structure for your reference.
We thus learned how to validate the file type in the Spring Boot Rest API by means of custom annotations.
If you are facing challenges when implementing the above solution do not hesitate to comment, and we will try to help you.
Thank you
Enjoy your learning!
0 Comments
If you have any doubts let me know.