51

Consider the following mapping:

@RequestMapping(value = "/superDuperPage", method = RequestMethod.GET) public String superDuperPage(@RequestParam(value = "someParameter", required = true) String parameter) { return "somePage"; } 

I want to handle the missing parameter case by not adding in required = false. By default, 400 error is returned, but I want to return, let's say, a different page. How can I achieve this?

6 Answers 6

74

If a required @RequestParam is not present in the request, Spring will throw a MissingServletRequestParameterException exception. You can define an @ExceptionHandler in the same controller or in a @ControllerAdvice to handle that exception:

@ExceptionHandler(MissingServletRequestParameterException.class) public void handleMissingParams(MissingServletRequestParameterException ex) { String name = ex.getParameterName(); System.out.println(name + " parameter is missing"); // Actual exception handling } 

I want to return let's say a different page. How to I achieve this?

As the Spring documentation states:

Much like standard controller methods annotated with a @RequestMapping annotation, the method arguments and return values of @ExceptionHandler methods can be flexible. For example, the HttpServletRequest can be accessed in Servlet environments and the PortletRequest in Portlet environments. The return type can be a String, which is interpreted as a view name, a ModelAndView object, a ResponseEntity, or you can also add the @ResponseBody to have the method return value converted with message converters and written to the response stream.

Sign up to request clarification or add additional context in comments.

3 Comments

@Ali this worked perfectly. thank you for your help. Also, is there a way of knowing which method threw this exception? I can't find it in printStack().
@peech The exception only encapsulates param type and param name information. So No, you can't know what method throws the exception.
@AliDehghani How to find the parameter names in case of multiple parameters missing?
22

An alternative

If you use the @ControllerAdvice on your class and if it extends the Spring base class ResponseEntityExceptionHandler. A pre-defined function has been created on the base class for this purpose. You have to override it in your handler.

 @Override protected ResponseEntity<Object> handleMissingServletRequestParameter(MissingServletRequestParameterException ex, HttpHeaders headers, HttpStatus status, WebRequest request) { String name = ex.getParameterName(); logger.error(name + " parameter is missing"); return super.handleMissingServletRequestParameter(ex, headers, status, request); } 

This base class is very useful, especially if you want to process the validation errors that the framework creates.

3 Comments

More verbose explanations and examples about @ContollerAdvice can be found in toptal.com/java/spring-boot-rest-api-error-handling
This worked for me. The accepted answer did not work as I was getting "Ambiguous @ExceptionHandler method mapped for..."
this approach is no longer valid for sprinboot 3 /spring 6. The method handleMissingServletRequestParameter is not overridable anymore.
7

You can do this with Spring 4.1 onwards and Java 8 by leveraging the Optional type. In your example that would mean your @RequestParam String will have now type of Optional<String>.

Take a look at this article for an example showcasing this feature.

Comments

2

I had this same issue today (30/08/2023) using Spring 3.1.3. You can solve this issue by overriding the protected function handleMissingServletRequestParameter of the ResponseEntityExceptionHandler base class.

@ControllerAdvice class DevBlogExceptionHandler : ResponseEntityExceptionHandler() { override fun handleMissingServletRequestParameter( ex: MissingServletRequestParameterException, headers: HttpHeaders, status: HttpStatusCode, request: WebRequest ): ResponseEntity<Any>? { val errorMessages = listOf("Required query parameter \"${ex.parameterName}\" is missing") val error = DevBlogException( status = HttpStatus.BAD_REQUEST, errors = errorMessages, timeStamp = LocalDateTime.now(), statusCode = status.value() ) return ResponseEntity<Any>(error, error.status); } } 

Comments

1

If you wan't to have a more clear error message on the client, too (which makes sense in case you don't have access to the logs, or some external consumer calls this method), you can do that by adding following to your application.yaml:

server: error: include-message: always 

This results in something like

{ ..., "status": 400, "error": "Bad Request", "message": "Required parameter 'firstName' is not present.", ... } 

(fiy: it's said that sensitive information can be disclosured by doing this, so think about if it's okay :) )

Comments

0

Maybe not that relevant, but I came across to a similar need: change the 5xx error to 4xx error for authentication header missing.

The controller is as follows:

@RequestMapping("list") public ResponseEntity<Object> queryXXX(@RequestHeader(value = "Authorization") String token) { ... } 

When you cURL it without the authorization header you get a 5xx error:

curl --head -X GET "http://localhost:8081/list?xxx=yyy" -H "accept: */*" HTTP/1.1 500 ... 

To change it to 401 you can

@ExceptionHandler(org.springframework.web.bind.MissingRequestHeaderException.class) @ResponseBody public ResponseEntity<Object> authMissing(org.springframework.web.bind.MissingRequestHeaderException ex) { log.error(ex.getMessage(), ex); return IResponse.builder().code(401).message(ex.getMessage()).data(null).build(); } @Data public class IResponse<T> implements Serializable { private Integer code; private String message = ""; private T data; ... } 

You can verify it by an automation test:

@Test void testQueryEventListWithoutAuthentication() throws Exception { val request = get("/list?enrollEndTime=1619176774&enrollStartTime=1619176774&eventEndTime=1619176774&eventStartTime=1619176774"); mockMvc.perform(request).andExpect(status().is4xxClientError()); } 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.