handling generic HTTP exception with custom message in spring REST application - rest

I am facing issues while handling HTTP exception in case of REST implementation
I want to display my custom Message to user in case of generic HTTP exception.
Trying to implement it using #ExceptionHandler annotation in controller advice, but that does not solve the problem.
I was looking into DefaultHandlerExceptionResolver but did not find any useful working example on the Net.
Can some one help me with this issue.

You can write your own application wide custom exception class, something like this.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFoundException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ResourceNotFoundException() {}
public ResourceNotFoundException(String message) {
super(message);
}
public ResourceNotFoundException(String message, Throwable cause) {
super(message, cause);
}
}
This custom exception can be thrown from any method then. For example:
protected void verifySomething() throws ResourceNotFoundException {
if(someConditionIsTrue()) {
throw new ResourceNotFoundException("Custom Message");
}
}

Related

How to make Spring Data JPA repository return an exception on no result?

I'm migrating an existing project to Spring Data JPA repositories.
In the old project my DAOs looks like this:
public class MovieDAOImp implements MovieDAO {
public Movie findByTitle(String title) throws NoResultException {
/... query the db, if no Movie, throw new NoResultException */
}
}
The new Spring JPA implementation looks like this ...
public interface MovieRepository extends JpaRepository<Movie, Integer> {
Movie findByTitle(String title);
}
In the new version findByTitle will return null if there's no Movie, but I want it to throw an Exception (preferably my NoResultException, but any exception will do).
The problem I have is all of my Services are implemented expecting the NoResultException to occur.
I don't want to re-work all of my Services to check for nulls.
Is there a way to get the JpaRepository to throw an exception when there is no result?
I was unable to find a direct answer to my question, so I came up with this workaround ...
I continue to implement my own DAO and use the repo internally.
This has the advantage of giving me complete control over DAO interface, including the exceptions that get thrown, so I don't have to re-work my services.
But it has the disadvantage of adding an extra layer.
public class MovieDAOImp implements MovieDAO {
#Autowired
private MovieRepository repo;
public Movie findByTitle(String title) throws NoResultException {
Movie ret = repo.findByTitle(title);
if (ret == null) {
throw new NoResultException("Unable to find movie by title '"+title+"'.");
}
return ret;
}
}
If anyone comes up with a better way to do this, please post an answer!

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?

How to remove/handle irrelevant or bad sort parameters from http url using Pageable interface in spring boot?
For e.g. I have a query like
http://localhost:8080/all?sort=firstName,asc&sort=nosuchfield,asc
How can I handle or remove the irrelevant field "nosuchfield"?
Also, how can I limit sort parameters in URL?
If the sorting field doesn't present in the database then below exception will be thrown by Spring JPA.
org.springframework.data.mapping.PropertyReferenceException: No property nosuchfield found for type <TYPE>!
at org.springframework.data.mapping.PropertyPath.<init>(PropertyPath.java:94)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:382)
at org.springframework.data.mapping.PropertyPath.create(PropertyPath.java:358)
However, the exception can be handled using various types. Ultimately, you can just log it or transform it into any custom exception. As per my requirement, I have transformed it into a custom exception.
Using AOP
#Aspect
#Component
public class UnKnownColumnSortingExceptionHandler {
#AfterThrowing(pointcut = "execution(* com.repositorypackage.*.*(..))", throwing = "exception")
public void executeWhenExceptionThrowninRepository(JoinPoint jp, Throwable ex) {
if (ex instanceof PropertyReferenceException) {
throw new CustomException("Invalid Database operation");
}
}
}
Using #ControllerAdvice(Exception handling in Application wise)
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public GlobalExceptionHandler() {}
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req) {
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Exception handling in Controller wise
Add the below piece of code to your controller
#ExceptionHandler({PropertyReferenceException.class})
public ResponseEntity<Void> handleAllExceptions(Exception ex, WebRequest req)
{
return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);
}

spring zuul print exception in ErrorController

When an exception thrown, finally it would be redirected to an error controller in ErrorController. But I could not find a way to print this exception.
#RestController
public class ErrorHandlerController implements ErrorController {
#Override
public String getErrorPath() {
return "/error";
}
#RequestMapping("/error")
public String error() {
// how to log this exception?
}
}
You can find exception in request attribute javax.servlet.error.exception. Zuul set exception object into request attribute like below.
request.setAttribute("javax.servlet.error.exception", exception);
So you can access this via HttpServletRequest object inside your controller.

How to catch ConstraintViolationExceptions in a REST Method

Before marking this as a duplicate: I read here and there that an ExceptionMapper will solve my problem, but for some reason it does not catch the ConstraintViolationException.
Update
The problem is solved: Using a separate, more specific ExceptionMapper works (one that implements ExceptionMapper< ConstraintViolationException >). But I don't fully understand why a more general exception mapper (one that implements ExceptionMapper< Exception >) does NOT catch my ConstraintViolationException.
Original question:
I am introducing bean validation to my REST Methods:
#PUT
public Response updateUser(#NotNull #Valid UserUpdateDTO userUpdateDTO) {
return ResponseUtil.ok(userService.updateUser(userUpdateDTO));
}
When a validation fails, I get a 400 response:
[PARAMETER]
[updateUser.arg0.initials]
[Initials must be between 3 and 5]
[AD]
I would like to catch the ConstraintViolationException before the response is sent because I have my own ResponseFactory.
Here is my ExceptionMapper (that works with my other exceptions!)
#Provider
public class ApiExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception e) {
Throwable cause = (e instanceof EJBException) && e.getCause() != null ? e.getCause() : e;
if (cause instanceof BadRequestException) {
logger.error("BadRequest", cause);
return ResponseUtil.badRequest(cause.getMessage());
} else if (cause instanceof ForbiddenException) {
logger.error("Forbidden", cause);
return ResponseUtil.forbidden(cause.getMessage());
} else if (cause instanceof ServerException) {
logger.error("ServerException", cause);
return ResponseUtil.serverError(cause.getMessage());
} else if (cause instanceof ConstraintViolationException) {
return ResponseUtil.badRequest("Validation failed");
}
// Default
logger.error("unexpected exception while processing request", cause);
return ResponseUtil.serverError(cause);
}
}
The ExceptionMapper is not even called when a validation problem occurs, and I get the default 400 error right away.
What am I doing wrong ? I suspect that it has something to do with the fact that the exception is not thrown within the method's body, but rather in its signature.
I am using Wildfly 11 RC and its default validation
Given a Rest Service such as:
#Stateless
#Path("/people")
public class PersonService {
#PersistenceContext(name = "people")
private EntityManager em;
#POST
#Path("/")
#Consumes(APPLICATION_JSON)
public Response create(#Valid Person person) throws DuplicateKeyException {
em.persist(person);
return Response.created(UriBuilder.fromResource(PersonService.class)
.path(PersonService.class, "getPerson")
.resolveTemplate("id", person.getId()).build())
.build();
}
}
then the following ExceptionMapper works just fine by itself:
#Provider
public class ConstraintViolationExceptionMapper implements ExceptionMapper<ConstraintViolationException>{
#Inject
private Logger logger;
private static class ConstraintViolationBean {
private final String propertyName;
private final String message;
private final String invalidValue;
private ConstraintViolationBean(ConstraintViolation constraintViolation) {
final StringBuilder propertyPath = new StringBuilder();
for (Path.Node node: constraintViolation.getPropertyPath()) {
if (propertyPath.length() > 0) {
propertyPath.append('.');
}
propertyPath.append(node.getName());
}
this.propertyName = propertyPath.toString();
this.message = constraintViolation.getMessage();
this.invalidValue = constraintViolation.getInvalidValue().toString();
}
public String getPropertyName() {
return propertyName;
}
public String getMessage() {
return message;
}
public String getInvalidValue() {
return invalidValue;
}
}
#Override
public Response toResponse(ConstraintViolationException exception) {
logger.log(Level.WARNING, "Constraint violation: {}", exception.getMessage());
List<ConstraintViolationBean> messages = new ArrayList<>();
for (ConstraintViolation cv : exception.getConstraintViolations()) {
messages.add(new ConstraintViolationBean(cv));
}
return Response.status(Response.Status.BAD_REQUEST)
.entity(messages)
.build();
}
}
This is real working (not production) code that I have been messing with for fun. There is also an ExceptionMapper for the DuplicateKeyException.
You can find the source on github at jaxrs-people, which is essentially an experiment.
One thing I have noticed is that EJBExceptions seem to be unwrapped before the ExceptionMapper is selected and invoked.
Update:
Now, if I add the following implementation of ExceptionMapper<Exception> to the deployment, then this one is invoked and the remaining exception mappers are ignored.
#Provider
public class GenericExceptionMapper implements ExceptionMapper<Exception> {
#Override
public Response toResponse(Exception exception) {
return Response.status(Response.Status.NOT_ACCEPTABLE)
.build();
}
}
Therefore it seems that because your ApiExceptionMapper is actually catching everything and your other ExceptionMappers will be ignored.
It looks like you need to either implement a separate ExceptionMapper for each of BadRequestException, ForbiddenException and ServerException, or some common ancestor class that is not Exception or RuntimeException.
I think that separate implementations would be better because code without if statements is easier to unit test.
What the Spec says:
§4.4 of "JAX-RS: Java™ API for RESTful Web Services (v2.0)" contains the statement:
When choosing an exception mapping provider to map an exception, an implementation MUST use the provider whose generic type is the nearest superclass of the exception.
This behaviour corresponds with what we have experienced here.

Unit-testing Jersey with Mocks

We have a test which extends JerseyTest and we need a mock service injected into it. How can we do this?
Thanks
Edit : this solution only works with Jersey 1.x, and is therefore quite outdated.
It refers to http://geek.riffpie.com/unit-testing-restful-jersey-services-glued-together-with-spring/ library.
If you use Spring, you can extend AbstractSpringAwareJerseyTest instead of JerseyTest, and inject whatever you need.
As requested, a little snippet of code :
public class MyClassTest extends AbstractSpringAwareJerseyTest{
#Autowired
private LdapSetupAndTearDown ldapSetupAndTearDown;
#Before
public void setUp2() throws Exception {
ldapSetupAndTearDown.setUp();
}
#After
public void tearDown2() throws Exception {
ldapSetupAndTearDown.tearDown();
}
public MyClassTest () throws Exception {
super(new WebAppDescriptor.Builder()
.contextPath("JSONUserServiceTest")
.contextParam("contextConfigLocation",
"classpath:/ctx-config-test.xml,classpath:/ctx-core.xml, classpath:/ctx-jmx-test.xml, classpath:ctx-jersey.xml, classpath:ctx-ldap.xml, classpath:ctx-ldap-test.xml")
.servletClass(SpringServlet.class).contextListenerClass(ContextLoaderListener.class).build());
}