Our controller looks like this -
#RestController
#RequestMapping(value="api")
#Validated
public class SampleController {
#RequestMapping(value = {"/test"}, method = RequestMethod.GET, produces = MediaType.APPLICATION_JSON_VALUE)
public void test(
#RequestParam(value = "testCode",required=true) String merchantCode
) throws Exception{
System.out.print("This is Test");
}
}
Here if we do not specify the required parameter "testCode" in our request we get "400 Bad Request", but the message section of the error response remains blank.
Response we are getting -
{"timestamp":1592441286607,"status":400,"error":"Bad Request","message":"","path":"/test"}
But expected is -
{"timestamp":1592441286607,"status":400,"error":"Bad Request","message":"Required String parameter 'testCode' is not present","path":"/test"}
We are using below Spring dependencies -
<spring-boot.version>2.3.0.RELEASE</spring-boot.version>
<spring-framework.version>5.2.6.RELEASE</spring-framework.version>
What I have seen is for this we are getting MissingServletRequestParameterException, but in the exception the message is coming as blank("").
I just updated the bootstrap.yml with server.error.include-message=always. It appears from Spring 2.3.0 the default behavior of Spring has changed. We can refer the following link from more details https://github.com/spring-projects/spring-boot/wiki/Spring-Boot-2.3-Release-Notes#changes-to-the-default-error-pages-content
Configuration in application.yml
server:
error:
include-message: always
include-binding-errors: always
include-stacktrace: on_trace_param
include-exception: true
Related
I am trying to make a postRequest and trying to get the object from request but the code is not getting executed. It is not getting inside the method.
I have already tried adding #componentScan in config files and #EntityScan
#RestController
#RequestMapping("/api/verify")
#CrossOrigin(origins = "*")
public class Verify {
#PostMapping(path = "/members", consumes = "application/json")
public String verify(#RequestBody DeviceDetails device) {
try {
System.out.println(device.getIpAddress());
//return "1";
return "hi";
}
catch (Exception e) {
System.out.println(e);
return "hi from err";
}
}
I expect that it should print hi to console and it should print IP Address
You're using wrong method - get (in the screenshot from postman), whereas in your mapping you're defining a post endpoint: #PostMapping(path = "/members", consumes = "application/json"). You need to change it to GetMapping or simply use post in postman. Also - check the urls and bodies if they match.
The above code has a path of /api/verify/members but in postman, it is /api/verify/call. In Postman, please correct it to /api/verify/members, change GET method to the POST method, and also please check if the JSON body is proper. Once these are fixed it should work.
I have a simple webservice that returns content either as json or as plain text (depending on the clients' accept http header).
Problem: if an error occurs during text/plain request, Spring somehow returns a 406 Not Acceptable. Which is kind of wrong, because spring could as well just write the error out as plain error text, and moreover should absolutely preserve the 400 error status:
#RestController
public class TestServlet {
#PostMapping(value = "/test", produces = {APPLICATION_JSON_VALUE, TEXT_PLAIN_VALUE, "text/csv"})
public Object post() {
throw new BadRequestException("bad req");
}
}
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException {
public BadRequestException(String msg) {
super(msg);
}
}
POST request with accept=application/json:
{
"timestamp": "2018-07-30T14:26:02",
"status": 400,
"error": "Bad Request",
"message": "bad req",
"path": "/test"
}
BUT with accept=text/csv (or text/plain) shows an empty response with status 406 Not Acceptable.
I also noticed the DispatcherServlet.processDispatchResult() is called twice: first with my BadRequest exception, 2nd time with HttpMediaTypeNotAcceptableException. So clearly the rendering of my custom exception fails, but why?
The problem is the restrictive Accept header allowing only one content type as response. In case of an error, Spring MVC needs to handle the BadRequestException and produce the required content type using a registered HttpMessageConverter.
By default Spring Boot has no message converter to produce text/plain directly from any object. You may register an ObjectToStringHttpMessageConverter (as a bean should work for Spring Boot) to allow this and you will get the result of BadRequestException.toString() as response body.
I assume a similar problem for text/csv but I am not sure how your setup for CSV message conversion looks like.
The condition written in "produces" determines the media type to use for the response to be "text/csv". So For a success scenario it works fine, **
but when you go for rendering an exception with a JSON body that
becomes a problem and gives you a 406 instead.
**
in latest versions of spring framework the problem fixes, but in old versions,as mentioned in Spring JIRA comments you should remove HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE attribute from request
the code might be like this :
#RestControllerAdvice
public class ExampleControllerAdvice {
#ExceptionHandler(value = Exception.class)
public ResponseEntity<?> handleException(HttpServletRequest request, Exception e) {
request.removeAttribute(
HandlerMapping.PRODUCIBLE_MEDIA_TYPES_ATTRIBUTE);
return new ResponseEntity<?>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
we can handle exception in advice
#ControllerAdvice
class ExceptionHandler{
#ExceptionHandler(value = {HttpMediaTypeNotAcceptableException.class})
public ResponseEntity handleMediaTypeException(HttpMediaTypeNotAcceptableException e) {
APIErrorResponse apiErrorResponse = new APIErrorResponse();
apiErrorResponse.setErrorCode("set custom code here");
apiErrorResponse.setErrorMessage("set custom meggage here/ here we can use message from object of exception i.e e.getMessage()");
return new ResponseEntity<>(errorDetails, HttpStatus.BAD_REQUEST);
}
}
Java Spring MVC. I can't open url without a parameter. I found suggestions in Internet(Spring MVC Thymeleaf Error: Parameter conditions not met for actual request parameters, http://www.baeldung.com/spring-requestmapping), but they didn't help me.
#Controller
#RequestMapping("/loans/")
public class LoanController {
#Autowired
LoanDAO loanDAO;
#GetMapping(value= "objectloan", params = {"loanTitle"})
public String index(Model theModel, HttpSession session, #RequestParam(value = "loanTitle", required = false, defaultValue = "") Optional<String> loanTitle)
{
....
}
URL works
http://localhost:8080/college/loans/objectloan?loanTitle=test
URL with error
http://localhost:8080/college/loans/objectloan
Error:
Type Status Report
Message Parameter conditions "loanTitle" not met for actual request parameters:
Description The server cannot or will not process the request due to something that is perceived to be a client error (e.g., malformed request syntax, invalid request message framing, or deceptive request routing).
Since loanTitle might not present in your query url,try to remove params = {"loanTitle"} in your controller method
#GetMapping(value= "objectloan")
public String index(Model theModel, HttpSession session, #RequestParam(value = "loanTitle", required = false, defaultValue = "") Optional<String> loanTitle)
{
....
}
I want to override the whitelabel error page. So as an example I have done this simple class:
#RestController
public class MyCustomErrorController implements ErrorController {
private static final String PATH = "/error";
#RequestMapping(value = PATH)
public String error() {
return "This is the error page";
}
#Override
public String getErrorPath() {
return PATH;
}
}
I have taken my example from here:
https://gist.github.com/jonikarppinen/662c38fb57a23de61c8b
According to that gist, it actually has a comment like this:
// Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
// Here we just define response body.
However that's not what I'm seeing. For instance if I hit to a URL that I know that it should respond me a 500 status code (intentional NullPointerException), then that's what I should see, but when I hit to that URL I get a 200 response back with my error message ("This is the error page")
If I don't use this custom controller, then it shows me a 500 error page with the stacktrace on it, which is the default behavior. I have seen an old issue opened in 2014 here:
https://github.com/spring-projects/spring-boot/issues/684 that someone mentioning the same problem, however their solution is to show explicitly 500 responses, which does not really pass through the HTTP response code.
Just for the record, I actually put a breakpoint to org.apache.catalina.connector.Response.sendError() method. When this custom error controller does not exist, I can clearly see that sendError() method is being called with a status 500. However if I were to add HttpServletResponse argument to my error() method I do not see that the instance has 500 status code set.
Explanation
In the website example you provided, the HttpStatus is retrieved from the injected HttpServletResponse.
So the following:
Appropriate HTTP response code (e.g. 404 or 500) is automatically set by Spring.
means
Spring sets it on the HttpServletResponse that he gets injected into his method as
argument.
He then has to retrieve the status and set it on his model ErrorJson.
Solution
To follow your example, you could change your method to this:
#RequestMapping(value = ERROR_MAPPING)
public ResponseEntity<String> error(HttpServletResponse response) {
return new ResponseEntity<String>("This is the error page",
HttpStatus.valueOf(response.getStatus()));
}
I used ResponseEntity<String> instead of defining a custom object (a.k.a. ErrorJson).
As I believe you know, alternatively to using the HttpServletResponse's status, you could just set yours with HttpStatus.
Here you are simply returning a message from one method, which is not an error as per SpringBoot.
Following method will help you to return http status code as you want :
#RequestMapping(value = PATH)
public ResponseEntity<Map<String, Object>> error() {
Map<String, Object> map = new HashMap<>();
String statusMessage = "This is the error page";
String statusCode = HttpStatus.BAD_REQUEST.value();
map.put(STATUS_CODE, statusCode);
map.put(STATUS_MESSAGE, statusMessage);
return ResponseEntity.badRequest().body(map);
}
I have this piece of code:
#RequestMapping(value = "/test.json", method = RequestMethod.GET)
#ResponseStatus(HttpStatus.OK)
public #ResponseBody Object[] generateFile(#RequestParam String tipo) {
Object[] variaveis = Variavel.getListVariavelByTipo(tipo);
return variaveis;
}
As far as I know it should take a request to test.json?tipo=H and return the JSON representation of Variavel[], however when I make such request I get:
HTTP Status 406 -
type Status report
message
descriptionThe resource identified by this request is only capable of generating responses with characteristics not acceptable according to the request "accept" headers ()
By using the following function I can get the expected json:
#RequestMapping(value = "/teste.json")
public void testeJson(Model model, #RequestParam String tipo) {
model.addAttribute("data", Variavel.getListVariavelByTipo("H"));
}
What I'm doing wrong?
#RequestBody/#ResponseBody annotations don't use normal view resolvers, they use their own HttpMessageConverters. In order to use these annotations, you should configure these converters in AnnotationMethodHandlerAdapter, as described in the reference (you probably need MappingJacksonHttpMessageConverter).