Spring MVC - HTTP status code 400 (Bad Request) for missing field which is defined as being not required - spring

I have Spring MVC application with this controller method.
#RequestMapping(value = "/add", method = RequestMethod.POST)
public String addNumber(#RequestParam(value="number", required=false) Long number) {
...
return "redirect:/showAll/";
}
In my JSP I have a standard HTML form which is posting a value named "number" to the controller method above. However, if I leave out the value (do not enter anything into the text field) and POST the data to the controller, before the controller method is called my browser shows
HTTP Status 400 - Required Long parameter 'number' is not present
although the controller method annotation clearly defines the "number"-parameter as not required.
Does anyone have a slight idea of what could be going on?
Thank you.
PS: The exception that is being thrown is as follows:
org.springframework.web.bind.MissingServletRequestParameterException: Required Long parameter 'number' is not present
EDIT: This is a Spring 3.2.3.RELEASE bug ( see here). With version 3.1.4.RELEASE I do not have this problem anymore.

I came across the same situation, and this happens when your parameter is present in the request with an empty value.
That is, if your POST body contains "number=" (with empty value), then Spring throws this exception. However, if the parameter is not present at all in the request, it should work without any errors.

My problem was that some of the headers in a request I was sending with Postman were not present (were unchecked):
When I checked back the Content-Length header, the request worked fine (200 OK response).

Related

Spring MVC - Error handling with redirect if download not possible

in my Spring Boot application you can download some documents, by pressing a button. This leads to the following user experience: If the user presses the button, either a download is triggered or nothing happens.
Currently, the backend is either returning the bytes or 404:
#GetMapping("/download")
public ResponseEntity<byte[]> download() {
Optional<byte[]> data = service.getDocumentData();
return data.map(bytes -> new ResponseEntity<>(bytes, headers(), OK))
.orElseGet(() -> ResponseEntity.notFound().build());
}
Now we want to achieve, that the user is redirected to an error page, if no file can be downloaded.
With Spring MVC I would just return the error template like
public String notFound() {
return "error/404"; // the custom template
}
But now I need to mix two different concerns. Returning template or returning a ResponseEntity. For this i stumbled above the following answer, which uses Generics. However, I don't think its a good practice. So i thought about other ways.
My first idea was to use HTTP location header for redirecting on the frontend side, but I never used it before. Do you think this is a good way? Do you have other ideas?
Suggestion 1:
In the download() method, add HttpServletResponse response as the parameter and call response.sendRedirect("http://somewhere/404.html"); when the document requested is not found.
In addition, you can also change the status code in the response as response.setStatus(HttpServletResponse.SC_NOT_FOUND);
Suggestion 2:
Throw a custom exception, for e.g. FileNotFoundException and handle the exception in the #ExceptionHandler to return the error page in the ResponseEntity.
Refer - https://howtodoinjava.com/spring-core/spring-exceptionhandler-annotation/

Get headers from #RequestHeader vs HttpServletRequest

What's the difference between read header data from #RequestHeader annotation vs HttpServletRequest?
The advantage of using Spring #RequestHeader is that it will automatically throw an exception like
HTTP Status 400 - Missing request header 'X' for method parameter of type, if the header is NOT sent in the input request (by setting required=true). An example usage shown below:
#RequestMapping(method=RequestMethod.GET)
public String users(#RequestHeader(required=true)String myHeader, Model model) {
//your Code
}
You can also set the default value for the header if you use #RequestHeader, you can refer here
We need to manually check the condition for header value !=null, throw the exception or set the default value, if you use HttpServletRequest, which will make your code verbose.
There is no difference in Performance. It is more convenient to use #RequestHeader than HttpServletRequest.getHeader().
This is similar to #RequestParam and HttpServletRequest.getParameter().
Spring has done the work of converting the request Headers, Parameters to method Parameters so that you can avoid boilerplate code.
There is a Spring written MethodArgumentResolver named RequestHeaderMethodArgumentResolver which in fact uses HttpServletRequest.getHeader() to get the header.

Url in a path variable spring restful service

When I am passing email address as path variable it is throwing following error
Console --> 2015-02-09 16:30:06,634 WARN - GET request for "http://localhost:8181/abc/users/testabghtmail#gmail.com" resulted in 406 (Not Acceptable); invoking error handler
Exception in thread "main" org.springframework.web.client.HttpClientErrorException: 406 Not Acceptable
at org.springframework.web.client.DefaultResponseErrorHandler.handleError(DefaultResponseErrorHandler.java:91)
at org.springframework.web.client.RestTemplate.handleResponseError(RestTemplate.java:607)
at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:565)
at org.springframework.web.client.RestTemplate.execute(RestTemplate.java:521)
at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:439)
at RestClient.main(RestClient.java:35)
I have tried lots of cases, so I finally found the problem with last domain like .com and .org which are internationalize domains. So instead of "testabghtmail#gmail.com" if I pass "testabghtmail#gmail.dom" it will work perfectly fine.
My code is
#RequestMapping(value = "users/{emailId:.*}", method = RequestMethod.GET)
public Object searchUser(#PathVariable("emailId") String emailId){
logger.info("Inside search user --> emailId " + emailId);
return userService.findUserByuserId(emailId);
}
I found no answer to this. I think it's an http rule we can't have domains at last in prameters and can make a request.
So work around to this is just pass a slash at the end of the url and there you go.
Like modify "http://localhost:8181/abc/users/testabghtmail#gmail.com/" with "http://localhost:8181/abc/users/testabghtmail#gmail.com". And thanks to spring rest architecture, it will automatically omit the last slash and you will get "testabghtmail#gmail.com" as a parameter value.
Let me know if you guys find something else.

Spring MVC Response already committed Issue

#RequestMapping(value="/getStats/{requestData}" , consumes=MediaType.APPLICATION_JSON_VALUE , method=RequestMethod.GET)
public void testRequest(
#PathVariable (value="requestData") GetStatsRequestBean getStats){
System.out.println("inside (testRequest)");
System.out.println(getStats);
}
In server log am getting
SRTServletRes W WARNING: Cannot set header. Response already committed.
And my app url is..
http://myhost:9080/myapp/getStats/{"startDate":"2013-10-05","endDate":"2013-10-05"}
Trying to set a response header after the response is committed causes this warning. The code you shared does not seem to do this. Are you sure you do not have any filter configured which tries to do this ? Your controller handler method return type is void which implies that you intend to generate the response yourself by using http servlet response object which needs to be provided in tehemethod parameter so that Spring can inject it and you can use this. As you have not provided the httpservlet response object in parameter, the actual view served would be implicitly determined by through a RequestToViewNameTranslator configured in your context.

Validate request headers with Spring validation framework

Is it possible to use the Spring validation framework with Spring MVC to validate the presence and value of an HTTP request header?
To check the presence of a request header, you don't need the validation framework. Request header parameters are mandatory by default, and if a mandatory header is missing in a request, Spring MVC automatically responds with 400 Bad Request.
So the following code automatically checks the presence of the header "Header-Name"...
#PostMapping("/action")
public ResponseEntity<String> doAction(#RequestHeader("Header-Name") String headerValue) {
// ...
}
... and if the header shall be optional, the annotation would need to be replaced by:
#RequestHeader(name = "Header-Name", required = false)
To check the value of a request header, the Spring validation framework can be used. To do this, you need to
Add #Validated to the controller class. This is a workaround needed until this feature is implemented.
Add the JSR-303 annotation to the request header parameter, e.g.
#RequestHeader("Header-Name") #Pattern(regexp = "[A-Za-z]*") String headerValue
Note however that this will result in a 500 in case of an invalid header value. Check this question for how to also get the correct status code (i.e. 400) for this case.
I don't see how this would be possible, since the validation framework only operates on your domain objects, not on the HTTP request itself. Specifically, the Validator interface doesn't specify any methods that take the HttpServletRequest object, which is what you'd need to have access to in order to grab the headers and test them.
Using the validation framework feels like the wrong solution to whatever problem you're trying to solve, especially since it's hard to know how there'd be a unique HTTP request header for a given form submission. Are you looking to test for an HTTP header that should always be present in requests to your app? Then you might want to consider implementing a HandlerInterceptor, which will intercept and process all requests to pages that you've mapped in any HanderMappings. Are you looking to test for an HTTP header that should always be present in any page view of your app? Then you'd want to implement a Filter, which operates outside of the context of Spring MVC.

Resources