#ExceptionHandler not getting called on exception - java

I'm migrating a SOAP service to REST. What I do is I have an wsdl definition, and I use that to automatically generate the request and response classes with JAXB.
This is my endpoint:
#Endpoint
public class RefundServiceEndpoint {
#Autowired
private RefundService refundService;
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = RefundException.class)
public void handleException(RefundException exception) {
// This should be called
}
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
#ExceptionHandler(value = UnableToMapException.class)
public void handleException(UnableToMapException exception) {
// This should be called
}
#Autowired
HttpServletRequest serverRequest;
#PayloadRoot(namespace = NAMESPACE_URI, localPart = "ReturnRequest")
#ResponsePayload
public ReturnResponse returnRequest(#RequestPayload ReturnRequest request) {
return this.refundService.partialRefund(request, serverRequest).getBody();
}
}
I'm throwing those two exceptions but the handlers don't get called.
The only way I managed to get them is with a try/catch but I'm trying to avoid that.
Thanks

Related

How to disable and throw exception for multiple endpoint in different Controller in Spring with annotation

I have a code like :-
public interface OrderControler {
#RequestMapping(value = "/trackinginfo",
produces = "application/json",
method = RequestMethod.GET)
// Disabling with annotation
default ResponseEntity<String> getTrackingInfo() {...}
default ResponseEntity<String> getTrackingInfo() {...}
}
}
public interface OrderControler {
// Disabling with annotation
#RequestMapping(value = "/orderDetails",
produces = "application/json",
method = RequestMethod.GET)
default ResponseEntity<String> getorderDetails()
}
#RequestMapping(value = "/orderMethods",
produces = "application/json",
method = RequestMethod.GET)
default ResponseEntity<String> getorderMethod() {...}
}
}
I want to implementate an annotation which throws 503 error for some of the endpoints in the different controller. I dont want to write if and else conditions to throw the Exceptions for 503 in each disabled endpoint.
Thanks
You can do it, of course, but why?
make a package on your project name like Exception.
after it, make the java class named it what is you need an exception.
example : notFoundException
package com.class3.poinofsaleclass3.exception;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
#ResponseStatus(value = HttpStatus.FOUND)
public class NotFoundException extends RuntimeException{
public NotFoundException (String message){
super(message);
}}
after it you can throw new NotFoundException your coding place
public static void checkNegative(int n) {
try {
int[] a = new int[n];
} catch (NegativeArraySizeException e) {
throw new IllegalArgumentException();
}
}
you can custom a annotation(examples CheckEndpointsAnnotion), write on these controlers. write a Interceptor extends HandlerInterceptorAdapter ,overwrite this method
preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
you can get the annotion or not by
HandlerMethod handlerMethod = (HandlerMethod) handler;
CheckEndpointsAnnotion checkAnnotation = handlerMethod.getMethodAnnotation(CheckEndpointsAnnotion.class);

How to Invoke a Spring MVC Interceptor When No Handler Match Is Found

I have a simple Spring Boot 2.1 application with a Spring Interceptor and #RestControllerAdvice.
My requirement is to have the Spring Interceptor be called for all situations, including when an exception occurs.
For custom exceptions, the Interceptor handler methods do get called, e.g. preHandle() and afterCompletion(). However, for exceptions handled by ResponseEntityExceptionHandler, the Spring Interceptor does not get called (I need ResponseEntityExceptionHandler's methods to create a custom ResponseBody to send back, however, I also need to trigger Interceptor's afterCompletion() for auditing purposes).
For instance, if a REST request is made with PATCH HTTP method, it only executes PersonControllerExceptionHandler.handleHttpRequestMethodNotSupported() and no PersonInterceptor is invoked.
Exception Handler:
#RestControllerAdvice
#Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonControllerExceptionHandler extends ResponseEntityExceptionHandler {
private final static Logger LOGGER = LoggerFactory.getLogger(PersonControllerExceptionHandler.class);
#ExceptionHandler(value = {PersonException.class })
public ResponseEntity<Object> handlePersonException(PersonException exception) {
LOGGER.info("Person exception occurred");
return new ResponseEntity<Object>(new Person("Bad Age", -1),
HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(value = {Exception.class })
public ResponseEntity<Object> handleException(Exception exception) {
LOGGER.info("Exception occurred");
return new ResponseEntity<Object>(new Person("Unknown Age", -100),
HttpStatus.INTERNAL_SERVER_ERROR);
}
#Override
public ResponseEntity<Object> handleHttpRequestMethodNotSupported(HttpRequestMethodNotSupportedException ex,
HttpHeaders headers,
HttpStatus status,
WebRequest request) {
LOGGER.info("handleHttpRequestMethodNotSupported()...");
return new ResponseEntity<>(new Person("Argh!", 900), HttpStatus.METHOD_NOT_ALLOWED);
}
}
The Interceptor:
#Order(Ordered.HIGHEST_PRECEDENCE)
public class PersonInterceptor extends HandlerInterceptorAdapter {
private static final Logger LOGGER = LoggerFactory.getLogger(PersonInterceptor.class);
#Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
LOGGER.info("PersonInterceptor#preHandler()...");
return true;
}
#Override
public void postHandle(HttpServletRequest request,
HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
LOGGER.info("PersonInterceptor#postHandler()...");
}
#Override
public void afterCompletion(HttpServletRequest request,
HttpServletResponse response, Object handler, Exception ex)
throws Exception {
LOGGER.info("PersonInterceptor#afterCompletion()...");
if (ex != null) {
LOGGER.error("afterCompletion(): An exception occurred", ex);
}
}
}
Registering the Interceptor:
#Configuration
public class AppConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new PersonInterceptor()).addPathPatterns("/person/*");
}
}
Controller:
#RestController
#RequestMapping("/")
public class PersonController {
private final static Logger LOGGER = LoggerFactory.getLogger(PersonController.class);
#Autowired
private PersonService personService;
#GetMapping(path = "/person/{age}", produces = MediaType.APPLICATION_JSON_VALUE)
public Person getPerson(#PathVariable("age") Integer age) throws PersonException {
LOGGER.info("Age: {}", age);
return personService.getPerson(age);
}
}
Initially I thought it has something to do with #Ordered but trying various scenarios where I give PersonInterceptor a higher precedence than #RestControllerAdvice yields the same undesirable outcome (or vice versa).
After digging into Spring framework, it seems like if a handler is not found, an exception is thrown back to DispacherServlet#doDispatch() which goes into a catch block, and therefore, it skips interceptor mapping process, including the afterCompletion() (I'm using Spring 5.1. as an example to trace the execution path):
DispacherServlet#doDispatch() is called and attempts is made to get the HandlerExecutionChain
I can see there are several HandlerMapping's; the one that fails is RequestMappingHandlerMapping
In AbstractHandlerMapping#getHandler(), it tries to get the handler via AbstractHandlerMethodMapping#getHandlerInternal()
Eventually, AbstractHandlerMethodMapping#lookupHandlerMethod() is called which fails to find a matching pattern due to the fact that there is no PATCH getPerson(), but rather GET getPerson()
At this point, RequestMappingInfoHandlerMapping#handleNoMatch() throws HttpRequestMethodNotSupportedException
This exception bubbles up to DispatcherServlet#doDispatch() exception clause which then processes by the exception resolver that it finds in
DispatcherServlet#processHandlerException() (of course, this finds an exception resolver and doesn't throw an exception which might trigger DispatcherServlet#triggerAfterCompletion() when an exception is caught in DispacherServlet#doDispatch() exception clause
Is there something I am missing to trigger the interceptor's afterCompletion() in cases when there is no handler match?

#Valid is not working with jax rs and springboot

I am getting NotFoundException while trying to implement custom exception handling in spring-boot rest application.
The code was working fine when I was using MVC (using #ControllerAdvice) annotations but not sure when I am sending a data which is violating the constraint mentioned in entity(pojo class) it is throwing only NotFoundException (for all validation failure) but not the MethodViolationException or ConstraintViolationException
I am not able to send the message for that particular violation.
Not sure where I am making this mistake. Please help
Code:
#POST
#Path("/customers/add")
public Response addCustomer(#Valid customer cust)
{
// Rest of the code
}
POJO:
#Entity
#Table(name="cust")
public class Customer
{
#NotNull
#Size(min=1,max=50,message ="invalid name")
String name;
}
Exception Handler:
#Provider
public class CustomHandler implements ExceptionMapper<Exception>
{
public Response toResponse(Exception ex)
{
if(ex instanceOf ConstraintViolationException)
{
Do something
}
}
**UPDATE 1
If I enable the send_error_in_response i am getting the message for this but not sure why my custom exception handler is not able to catch this exception and only throwing NotFoundException
Try Handling Exception Using:
#ControllerAdvice
#RestController
public class CustomizedResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(StudentNotFoundException)
public final ResponseEntity<ErrorDetails> handleUserNotFoundException(StudentNotFoundException ex, WebRequest request) {
ErrorDetails errorDetails = new ErrorDetails(new Date(), ex.getMessage(),
request.getDescription(false));
return new ResponseEntity<>(errorDetails, HttpStatus.NOT_FOUND);
}
For more information you might want to refer http://www.springboottutorial.com/spring-boot-validation-for-rest-services

Spring Boot - how to handle UnrecognizedPropertyException from Jackson manually?

So I have a controller that takes input a request body:
#RestController
#RequestMapping("/foo/bar")
public class FooBarController {
#RequestMapping(method = RequestMethod.POST)
public ResponseEntity<?> doTheFoo(#RequestBody MyDto dto) {
...
}
}
And I have this FooBar:
public clas FooBar {
#JsonProperty("foo")
private String foo;
public void setFoo(String foo) { ... }
public String getFoo() { ... }
}
And I have set in my properties to fail on unknown properties:
spring.jackson.deserialization.fail-on-unknown-properties: true
And I also have a handler to try and capture the failure:
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(UnrecognizedPropertyException.class)
public ResponseEntity<?> handleUnrecognizedPropertyException(UnrecognizedPropertyException ex) {
...
}
}
But when I POST to my endpoint with an unknown property (e.g. {"bar": "baz"}), nothing in the GlobalExceptionHandler is run and instead a vanilla 400 BAD REQUEST is returned. How do I intercept the unknown property exception and provide a different response?
if you are using properly the property and your controller works well, you could intercept the HttpMessageNotReadableException exception.
Try BasicErrorController of Spring Boot. This should look something like this.
#Controller
#RequestMapping
public class ErrorController {
#RequestMapping( value = "/error/404.html" )
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public String pageNotFoundError( HttpServletRequest request ) {
"errors/404"
}

where to throw the HTTP Runtime Exception

Let's say I have the following runtime exception:
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public class ExpectationsFailedException extends RuntimeException {
public ExpectationsFailedException(String message) {
super(message);
}
}
My question is if it is ok to throw the previous HTTP exception in my service layer or should I throw it from my controller:
#Service
public class UserService {
#Autowired
...
public void addUser(final String email, final String username, final String password){
if(parameters_are_not_valid){
throw new ExpectationsFailedException("Invalid input");
}
}
}
The controller exception throwing solution would be the following:
#Service
public class UserService {
#Autowired
...
public void addUser(final String email, final String username, final String password) throws InvalidInputParameters {
if(parameters_are_not_valid){
throw new InvalidInputParameters("Invalid input");
}
}
}
and in my controller
#RestController
public class XController{
#Autowired
private UserService userService;
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public void addUser(#Valid #RequestBody SignUpForm form, BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ExpectationsFailedException("Input parameters conditions were not fulfilled");
}
try {
userService.addUser(...);
}
catch(InvalidInputParameters ex){
throw new ExpectationsFailedException("Invalid service input parameters");
}
}
}
Which of those solutions is preferred? Why? I have a feeling that I should not throw HTTP exceptions in my services because I may use that services in other contexts which may not be related to HTTP.
I would go with the second one.
What do you think?
I agree with your last statement. Your service layer should be independent of HTTP or frontent frameworks (#ResponseStatus is Spring MVC annotation and therefore it's not the best practice to use it in your service layer).
However you don't have to throw one exception in service layer, catch it in controller and rethrow another exception annotated with #ResponseStatus. Just add exception handler for the service exception and return appropriate response status from it. You have plenty of options, for instance #ExceptionHandler:
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
#ExceptionHandler(InvalidInputParameters.class)
public void handle() {
// Do nothing, just return the status
}
You can put this code to #ControllerAdvice annotated class to enable it for all controllers or just in you controller if it's not needed elsewhere.

Categories