Search code examples
springspring-bootexceptionhandlerspring-restcontroller

SpringBoot handle @RequestParam exception


I have controller and method deleteFolder has @RequestParam:

@RestController
    public class FolderController extends BaseController {

@DeleteMapping(path = Const.APIVersions.API_V1 + "/folders", params = "id")
public ResponseEntity<HttpStatus> deleteFolder(KeycloakAuthenticationToken authentication, @RequestParam Long id) throws EntityNotFoundException {
    folderService.deleteFolder(id, authentication.getName());
    return ResponseEntity.ok(HttpStatus.OK);
}

parent class:

public abstract class BaseController {
    @ExceptionHandler({MissingServletRequestParameterException.class,
            UnsatisfiedServletRequestParameterException.class, MethodArgumentTypeMismatchException.class,
            IllegalArgumentException.class})
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<ErrorResponse> handleMissingServletRequestParameter(MissingServletRequestParameterException exception) {
        return ResponseEntity
                .status(HttpStatus.BAD_REQUEST)
                .body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), exception.getMessage()));
    }

I want to handle exception, when required param is missing. But that exception handler don't want to catch that exception raised from deletFolder method.

Any others handlers works.


Solution

  • You have to create custom ConstraintValidator and catch ConstraintViolationException so your base controller becomes

    
    import org.springframework.http.*;
    import org.springframework.web.bind.annotation.*;
    
    import javax.validation.*;
    
    public abstract class BaseController {
    
    
        @ExceptionHandler({ConstraintViolationException.class})
        @ResponseStatus(HttpStatus.BAD_REQUEST)
        public ResponseEntity<ErrorResponse> handlerFolderNotFoundException(ConstraintViolationException exception) {
            return ResponseEntity
                    .status(HttpStatus.BAD_REQUEST)
                    .body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), exception.getMessage()));
        }
    }
    

    Validation Annotation

    
    import javax.validation.*;
    import java.lang.annotation.*;
    
    @Constraint(validatedBy = FolderValidationConstraint.class)
    @Target({ElementType.PARAMETER, ElementType.FIELD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface ValidateFolder {
    
        String message() default "Error validating folder name";
    
        Class<?>[] groups() default {};
    
        Class<? extends Payload>[] payload() default {};
    
    }
    

    Validator class

    import lombok.extern.slf4j.*;
    
    import javax.validation.*;
    import java.io.*;
    
    @Slf4j
    public class FolderValidationConstraint implements ConstraintValidator<ValidateFolder, String> {
    
        @Override
        public boolean isValid(String path, ConstraintValidatorContext constraintValidatorContext) {
            return new File("/home/ashish/Documents/test-delete/" + path).exists();
        }
    }
    

    Controller

    import org.springframework.validation.annotation.*;
    import org.springframework.web.bind.annotation.*;
    
    import java.io.*;
    import java.util.*;
    
    @Validated
    @RestController
    @RequestMapping(path = "/folders")
    public class FolderController extends BaseController {
    
        @GetMapping
        public Map<String, Boolean> delete(@ValidateFolder @RequestParam("name") String folderName) {
            final File path = new File("/home/ashish/Documents/test-delete/" + folderName);
            return Map.of("exists", path.exists());
        }
    }
    

    Please make sure to add dependency in pom.xml

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-validation</artifactId>
    </dependency>