peech
peech

Reputation: 1001

Java Spring - how to handle missing required request parameters

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?

Upvotes: 51

Views: 89859

Answers (6)

CKey
CKey

Reputation: 317

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 :) )

Upvotes: 1

Alaksion
Alaksion

Reputation: 21

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);
    }

}

Upvotes: 2

Jeff Tian
Jeff Tian

Reputation: 5923

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());
}

Upvotes: 0

Eric Giguere
Eric Giguere

Reputation: 786

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.

Upvotes: 22

dimitrisli
dimitrisli

Reputation: 21421

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.

Upvotes: 7

Ali Dehghani
Ali Dehghani

Reputation: 48213

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.

Upvotes: 74

Related Questions