Reputation: 1001
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
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
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
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
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
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
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, theHttpServletRequest
can be accessed in Servlet environments and thePortletRequest
in Portlet environments. The return type can be aString
, which is interpreted as a view name, aModelAndView
object, aResponseEntity
, 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