简体   繁体   中英

How do I get the details of multiple missing request parameters from a Validated Spring RestController?

I'm attempting to use Spring's validation of two @RequestParam s, with an @ControllerAdvice catching the exceptions thrown by the framework when a parameter is missing, and returning a 400 error with the missing parameter.

So, my code looks like:

@RestController
@Validated
public class FooController {
  @RequestMapping(value = "/foo", method = RequestMethod.GET)
  @ResponseBody
  public Foo getFoo(@RequestParam LocalDate dateFrom, @RequestParam LocalDate dateTo) {
    // Do stuff
  }
}

@ControllerAdvice
public class ExceptionController {
  @ExceptionHandler(value = {MissingServletRequestParameterException.class})
  @ResponseStatus(value = HttpStatus.BAD_REQUEST)
  @ResponseBody
  public ErrorResponse handleMissingParameterException(MissingServletRequestParameterException ex) {
    return new ErrorResponse(ex.getMessage());
  }
}

This works perfectly if I miss a single parameter - I get a nice JSON response that looks like:

{
  "reason": "Required LocalDate parameter 'dateFrom' is not present"
}

with a 400 status.

However if I miss both parameters, I get the same error response as above - ie it's only reporting the first missing parameter, where I'd prefer it if I can list all of them.

Looking at the method of the exception, it seems like it only intends to handle a single parameter - it has methods getParameterName() and getParameterType() in the singular.

Is there any way I can get Spring to report all validation errors in a single exception to improve the experience for the client?

Instead introduce a one POJO filter with validate annotations

public class Filter {
    @NotNull
    private LocalDate dateFrom;
    @NotNull
    private LocalDate dateTo;
}

and then just use it in the controller

public Foo getFoo(@Valid Filter filter) {
  // Do stuff
}

BindingResult will provide all validation errors

You can set @RequestParam(required = false) and then use @NotNull .

If a parameter is null then a ConstraintViolationException will be thrown. To handle it just declare an @ExceptionHandler method. Check the code snippet below

@RestController
@Validated
public class FooController {
  @RequestMapping(value = "/foo", method = RequestMethod.GET)
  @ResponseBody
  public Foo getFoo(
      @RequestParam (required = false) 
      @NotNull(message = "Required LocalDate parameter dateFrom is not present") 
      LocalDate dateFrom,
      @RequestParam (required = false) 
      @NotNull(message = "Required LocalDate parameter dateTo is not present") 
      LocalDate dateTo) 
  ) {
    // Do stuff
  }
}

@ControllerAdvice
public class ExceptionController {
    @ExceptionHandler({ConstraintViolationException.class})
    public ResponseEntity<List<ErrorResponseDTO>> exceptionHandler(ConstraintViolationException e) {
        List<ErrorResponseDTO> errors = new ArrayList<>(e.getConstraintViolations().size());
        e.getConstraintViolations().forEach(violation -> errors.add(new ErrorResponseDTO(violation.getMessage())));
        return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST);
    }
}

Outcome will be like this:

[
    {
        "reason": "Required LocalDate parameter dateFrom is not present"
    },
    {
        "reason": "Required LocalDate parameter dateTo is not present"
    }
]

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM