How can I get the RequestBody in GlobalExceptionHandler class using springboot - java

===============controller class==================
#PostMapping(path = "/products")
public ResponseEntity<ProductResponseDTO> saveProduct(#RequestBody Product product, #RequestHeader(name = "uuid", required = false) String uuid, HttpServletRequest request) throws Exception {
request.setattribute("product",product);
productRepo.save(product);
......
.....
}
=======================Exception handler class==========================
#ControllerAdvice
public class GlobalErrorHandler{
#ExceptionHandler({RuntimeException.class})
public ResponseEntity<ErrorDetails> generateRuntimeException(RuntimeException re, WebRequest request, HttpServletRequest servletRequest) throws IOException {
ErrorDetails errorDetails = new ErrorDetails();
Product product=(Product)servletRequest.getAttribute("product");
errorDetails.setTimestamp(LocalDateTime.now());
errorDetails.setMessage("500");
errorDetails.setDetails(re.getMessage());
log.error("Exception occured3: " + re);
}
return new ResponseEntity<ErrorDetails>(errorDetails, HttpStatus.INTERNAL_SERVER_ERROR);
}
]
I can see while executing the request.getAttribute("product"), it is showing null value. Once the exception happens, it is not getting inside the controller body, directly passing the control to exception class. As a result, resuest.setAttribute("product") is not setting the value to the request.
I Googled and tried several ways but still getting null values.
Anyone please help me to get it resolved.

Related

Java/Spring: Override default #RequestBody functionality

So I have this API:
public Map<String, Object> myFunc(#RequestBody #Valid MyPrivateEntity body) {}
Which is marked with #RequestBody and #Valid
The thing is, if I omit the body when calling this API, I get the following error message:
{
"title": "Failed to parse request",
"detail": "Required request body is missing: public com.privatePackage.misc.service.rest.MyPrivateEntity com.privatePackage.misc.service.rest.MyPrivateResource.myFunc(java.lang.String, com.privatePackage.misc.service.rest.MyPrivateEntity)",
"status": 400
}
I don't want the error message to include class names and paths, instead just "Required request body is missing".
How can I do that?
Thanks
Try this code
#ExceptionHandler(BindException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST) // return 400 if validate fail
public String handleBindException(BindException e) {
// return message of first error
String errorMessage = "Request not found";
if (e.getBindingResult().hasErrors())
e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
return errorMessage;
}
Or use this way
public Map<String, Object> myFunc(
#RequestBody #Valid MyPrivateEntity body,
BindingResult bindingResult) { // add this parameter
// When there is a BindingResult, the error is temporarily ignored for manual handling
// If there is an error, block it
if (bindingResult.hasErrors())
throw new Exception("...");
}
Reference:
https://www.baeldung.com/spring-boot-bean-validation
If you need more control on only this endpoint then I'll suggest to mark request body optional and check in the method if it's null then return whatever message you want to show.
#RequestBody(required = false)
Try #ControllerAdvice to customise your message.
#ControllerAdvice
public class RestExceptionHandler extends ResponseEntityExceptionHandler {
#Override
protected ResponseEntity<Object> handleHttpMessageNotReadable(
HttpMessageNotReadableException ex, HttpHeaders headers,
HttpStatus status, WebRequest request) {
// paste custom hadling here
}
}
Reference:
https://ittutorialpoint.com/spring-rest-handling-empty-request-body-400-bad-request/

HttpMessageNotReadable Exceotion: JSON parse error: cannot deserialize instance of ArrayList out of start object token

I have an endpoint
#GetMapping(value = "/accounts/{accountId}/placementInfo", headers = "version=1")
#ResponseStatus(HttpStatus.OK)
public List<PlacementDetail> findPlacementDetailByPlacementInfoAtTime(#PathVariable("accountId") Long accountId,
#RequestParam(value = "searchDate", required = false)
#DateTimeFormat(iso = DateTimeFormat.ISO.DATE) LocalDate searchDate) {}
And I am sending the request by using rest template
placementResponseEntity = restTemplate.exchange(placementUriBuilder(accountId, searchDate), HttpMethod.GET,
apiRequestEntity,new ParameterizedTypeReference<List<PlacementDetail>>() {});
with a helper method
private String placementUriBuilder(long accountId, LocalDate searchDate) throws IOException {
String resourceUri = ACCOUNT_RESOURCE_URI_START + accountId + PLACEMENT_DETAIL_RESOURCE_URI_END;
String url;
if(searchDate != null) {
url = UriComponentsBuilder.fromUri(serverUri.getUri()).path(resourceUri).queryParam("searchDate", searchDate.format(DateTimeFormatter.ISO_DATE)).build().toUriString();
} else {
url = UriComponentsBuilder.fromUri(serverUri.getUri()).path(resourceUri).build().toUriString();
}
return url;
}
When I look at the SO people talk about sending the object and failing as the created JSON is in wrong format but here this is a get api and I do not understand the source of the problem.
This is commonly caused by a missing error handler on your RestTemplate. Your server responds with an error and your client tries to deserialize it to a List<PlacementDetail>. In order to address this, you should properly handle HTTP error codes.
See the below snippet.
#Configuration
public class ClientConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder restTemplateBuilder) {
return restTemplateBuilder
.errorHandler(new ClientErrorHandler())
.build();
}
public class ClientErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
// check if HTTP status signals an error response
return !HttpStatus.OK.equals(httpResponse.getStatusCode());
}
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
// handle exception case as you see fit
throw new RuntimeException("Error while making request");
}
}
}

Spring #ExceptionHandler does not return content unless body is empty

I'm using Spring #ControllerAdvice to handle exceptions
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { DataIntegrityViolationException.class})
#ResponseBody
public ResponseEntity<String> unknownException(Exception ex, WebRequest req) {
return new ResponseEntity<>(ex.getCause().getMessage(), new HttpHeaders(), HttpStatus.INTERNAL_SERVER_ERROR);
}
The problem i'm experiencing is that when the exception occurs (when i send a request via swagger), i do not get an expected exception message, but :
{"error": "no response from server"}
Response Code : 0
Response Body : No Content
I can clearly see in debug mode that the method annotated by #ExceptionHandler is called.
I've experimented with method return types, #ResponseBody, #ResponseStatus annotations and a few other thing that came to mind, but it seems that i only get some non-empty response when i return a ResponseEntity without a body, e.g.
ResponseEntity.noContent().build()
or
ResponseEntity.ok().build()
In such cases i get correct http code and a few headers
Please advise on what i'm doing wrong
Spring version 4.3.9
Spring boot version 1.5.4
Thank you in advance
UPD
I carried on experimenting and this is the solution that worked for me.
It is quite close to one of the answers - i will mark that one as accepted
In short, i just created my own dto class , populated the instance with the exception details i was interested in and returned it directly
My code
#ExceptionHandler(value = { DataIntegrityViolationException.class})
#ResponseStatus(code = HttpStatus.INTERNAL_SERVER_ERROR)
#ResponseBody
public ExceptionDetailHolder unknownException(Exception ex, WebRequest req) {
final Throwable cause = ex.getCause();
return new ExceptionDetailHolder("Error interacting with the database server",
cause.getClass() + ":" + cause.getMessage(),
cause.getCause().getClass() + ":" + cause.getCause().getMessage()
);
}
#AllArgsConstructor
#NoArgsConstructor
#Getter
#Setter
private class ExceptionDetailHolder {
private String message;
private String exceptionMessage;
private String innerExceptionMessage;
}
Results (which also show the contents of ex.getMessage and ex.getCause().getMessage() as asked by commenters) :
{
"message": "Error interacting with the database server",
"exceptionMessage": "class org.hibernate.exception.ConstraintViolationException:could not execute statement",
"innerExceptionMessage": "class com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException:Column 'allow_copay_payments' cannot be null"
}
My way of handling exception is like below, I find the specific exception and then create my own class object ValidationErrorDTO in this case, then populate required fields in that class (ValidationErrorDTO):
#ExceptionHandler(HttpMessageNotReadableException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ResponseBody
public ResponseEntity<ValidationErrorDTO> processValidationIllegalError(HttpMessageNotReadableException ex,
HandlerMethod handlerMethod, WebRequest webRequest) {
Throwable throwable = ex.getMostSpecificCause();
ValidationErrorDTO errorDTO = new ValidationErrorDTO();
if (throwable instanceof EnumValidationException) {
EnumValidationException exception = (EnumValidationException) ex.getMostSpecificCause();
errorDTO.setEnumName(exception.getEnumName());
errorDTO.setEnumValue(exception.getEnumValue());
errorDTO.setErrorMessage(exception.getEnumValue() + " is an invalid " + exception.getEnumName());
}
return new ResponseEntity<ValidationErrorDTO>(errorDTO, HttpStatus.BAD_REQUEST);
}

Change mediatype of Spring errors

I am running a simple spring-boot web application api. The problem is when I throw an exception, or spring throws an exception, the exception is always thrown in Http, springs default error page.
Is there a way to get the errors to default to another mediatype, say, JSON?
Basically I always want json, even on errors.
I do not want to have to write a custom #ExceptionHandler for each exception type as that is just plain terrible..
Update: Here is what I am currently trying:
#ControllerAdvice
#EnableAutoConfiguration
public class ErrorWritter extends ResponseEntityExceptionHandler {
#Override
public ResponseEntity<Object> handleExceptionInternal(Exception ex, Object body, HttpHeaders headers, HttpStatus status, WebRequest request) {
String response = "{\"status\":\""
+ status.toString()
+ "\",\"generic message\":\""
+ status.getReasonPhrase()
+ "\",\"specific message\":\""
+ ex.getMessage()
+ "\" }";
return new ResponseEntity<Object>(response, headers, status);
}
}
This doesn't seem to do anything however. Is there something I need to do in order to get spring to recognize that I want it to use this?
Please note: I am using Java config and NOT xml config.
There is pretty good info in the following article:
http://spring.io/blog/2013/11/01/exception-handling-in-spring-mvc
you can create a model for your error such as:
public class ErrorInfo {
public final String url;
public final String ex;
public ErrorInfo(String url, Exception ex) {
this.url = url;
this.ex = ex.getLocalizedMessage();
}
}
And an error handler that uses that returns a representation of that model:
#ResponseStatus(HttpStatus.BAD_REQUEST)
#ExceptionHandler(MyBadDataException.class)
#ResponseBody ErrorInfo handleBadRequest(HttpServletRequest req, Exception ex) {
return new ErrorInfo(req.getRequestURL(), ex);
}
If you want more details on how the #ExceptionHandler works in spring, look at the spring docs:
http://docs.spring.io/spring/docs/3.2.x/spring-framework-reference/html/mvc.html#mvc-exceptionhandlers

Is there a way for ExceptionHandlers to output an argument when an unchecked exception is thrown?

#Component
public class Fetcher {
private void sendGet(String url) {
someMethodThatCanThrowUncheckedException();
}
}
#Controller
public class TestController {
#Autowired
private Fetcher fetcher;
#RequestMapping(value = {"/content"}, method = RequestMethod.GET)
#ResponseBody
public String getContent(#RequestParam(value = "url", required = true) String url) {
fetcher.sendGet(url);
return "Success";
}
#ExceptionHandler({Exception.class})
#ResponseBody
public String handleUncaughtException(final Exception exception) {
return "An internal error has occured trying to fetch url: " + url;
}
}
I would like a way to be able to output the variable url in handleUncaughtException. However, since the exception comes from another class Fetcher, is there any way I can know what the url parameter was that likely caused this unchecked exception?
Correct me if I'm wrong, but I believe I can't just store the url as a field in TestController, because I'm calling the /content endpoint concurrently.
What you can do is set the url in the request attributes and retrieve it in your #ExceptionHandler.
#RequestMapping(value = {"/content"}, method = RequestMethod.GET)
#ResponseBody
public String getContent(#RequestParam(value = "url", required = true) String url, HttpServletRequest request) {
request.setAttribute("url", url);
fetcher.sendGet(url);
return "Success";
}
And retrieve it
#ExceptionHandler({Exception.class})
#ResponseBody
public String handleUncaughtException(final Exception exception, HttpServletRequest request) {
String url = (String) request.getAttribute("url");
if (url != null)
return "An internal error has occured trying to fetch url: " + url;
else
return "something else";
}
You should probably make the attribute key something constant.

Categories

Resources