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.
Related
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);
}
I am trying integrate my current project with a external authentication API, and right now my goal it is redirect to a external url:
https://auth.mercadolivre.com.br/authorization?response_type=code&client_id=$APP_ID
where the autorization process takes place, after that it's redirect back to my application, with an url like that:
http://YOUR_REDIRECT_URI?code=SERVER_GENERATED_AUTHORIZATION_CODE
where I need store this code variable internally.
I got this code so far, based on the examples available here and here:
public String getCode() throws ApiException, URISyntaxException {
Client client = ClientBuilder.newClient();
WebTarget resourceTarget = client.target(getApi().getLocation());
// Build a HTTP GET request that accepts "text/plain" response type
// and contains a custom HTTP header entry "Foo: bar".
Invocation invocation = resourceTarget.request("text/plain").buildGet();
// Invoke the request using generic interface
String response = invocation.invoke(String.class);
return response;
}
#POST
public Response getApi() throws ApiException, URISyntaxException {
getAuthUrl();
URI targetURIForRedirection = new URI(auth_url);
return Response.temporaryRedirect(targetURIForRedirection).build();
}
But, despite the application reaching the destination, instead of being open in the browser, the html is dumped on the console and an error is issued (something like an invalid character on the code dumped on the console).
I just wan, from the methods above, redirect the user to the authorization page (first link), and when the process ends, execute the rest of the code, storing the value returned for future uses.
For reference, this code it is called from the AuthenticationManager in my spring-security layer. The implementation I got so far:
#Configuration
#EnableWebSecurity
public class Security extends WebSecurityConfigurerAdapter {
#Bean
#Override
public AuthenticationManager authenticationManagerBean() throws Exception {
return new AuthManager();
}
#Override
public void configure(HttpSecurity http) throws Exception {
...
}
#Override
public void configure(WebSecurity web) throws Exception {
...
}
public class AuthManager implements AuthenticationManager {
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
MercadoLivre mercadoLivre = new MercadoLivre();
try {
mercadoLivre.getAccessToken();
UserResponse data = (UserResponse) mercadoLivre.GET("/users/"+mercadoLivre.getUserId().toString());
return new AuthResponse(data);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
}
public class AuthResponse implements Authentication {
...
}
}
the method is called from inside getAccessToken().
In restEasy I used this BadRequestExcrption to throw the error but in Spring Rest it's not working. Please specify any method to throw 400 error in Spring Rest.
Make your own BadRequestException class and throw it.
Here is an example:
#ResponseStatus(HttpStatus.BAD_REQUEST)
public class BadRequestException extends RuntimeException{
public BadRequestException() {
super();
}
public BadRequestException(final String message, final Throwable cause) {
super(message, cause);
}
public BadRequestException(final String message) {
super(message);
}
public BadRequestException(final Throwable cause) {
super(cause);
}
}
You can specify your own status code in #ResponseStatus
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.
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");
}
}