WebApplicationException weirdness - jersey

I've extended WebApplicationException with an UnauthorizedException:
public class UnauthorizedException extends WebApplicationException {
My REST classes extend a base class that implements the authCheck, a subclass method:
try{
authCheck();
RecordingList recordings = CODIRecording.getRecordings(type, timeframe);
return Response.ok().entity(recordings).build();
}catch(WebApplicationException e){
throw e; // Results in 500
throw new UnauthorizedException(); // Results in 401
}
When the authCheck fails it throws UnauthorizedException. If the subclass method doesn't have the catch/try (the exception just propagates out from authCheck) or if it re-throws the exception the client receives a 500.
If the method's catch throws a NEW UnauthorizedException the client receives a 401 as expected.
Is this "normal" behavior? It seems odd.

I would guess that what you are actually catching there is not UnauthorizedException, but some other WebApplicationException. Did you try debugger? Or simply do e.printStackTrace() in the catch clause to be sure.

Related

How to apply #ControllerAdvice from a non-controller?

Is there a way in which we can generate the JSON response from within the code that an API endpoint would show when a controller is called.
I basically want the application to call one of its own controllers and generate the response as a JSON string but not by making an HTTP call.
Calling the controller method and applying an objectmapper on it gives back the results just fine, but when there are exceptions it throws it through. However, I want it to generate the error response that would have generated from #ControllerAdvice exception handlers.
Is it possible to apply the exception handlers defined in #ControllerAdvice to an exception caught in a variable from within the application (Not by making an HTTP call)?
I am building a bulk api so that instead of the user calling an api multiple times, they can just put a list of payloads to this new bulk api, and internally I want to save the responses of each subrequest within the bulk api's db entity itself, and for that I need the exact json responses for each subrequest as if the original api was called.
I can do an exception handling separately for this bulk api code but then that would duplicate the code I already have in my controller advice.
One solution I have come up with is given below. However, if I use it I'll have to remove all my current exception handlers and just use one single exception handler accepting Exception.
Controller Advice:
#ExceptionHandler(ConversionFailedException.class)
public ResponseEntity<ErrorResponse> handleConversionFailedException(final ConversionFailedException e) {
return buildResponseEntity(HttpStatus.BAD_REQUEST, e);
}
#ExceptionHandler(value = {ActionNotFoundException.class})
public ResponseEntity<ErrorResponse> handleActionNotFoundException(ActionNotFoundException e) {
return buildResponseEntity(HttpStatus.NOT_FOUND, e);
}
// someone just added it
#ExceptionHandler(value = {NewRuntimeException.class})
public ResponseEntity<ErrorResponse> handleNewRuntimeException(NewRuntimeException e) {
return buildResponseEntity(HttpStatus.INTERNAL_SERVER_ERROR, e);
}
Bulk API code:
try {
}catch(ConversionFailedException e){
return buildResponseEntity(HttpStatus.BAD_REQUEST, e);
}catch(ActionNotFoundException e){
return buildResponseEntity(HttpStatus.NOT_FOUND, e);
}
// they forgot to add it here
My current solution:
Controller Advice:
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleAllExceptions(final Exception e) {
return ExceptionHandlerUtil.funcA(e);
}
ExceptionHandlerUtil class (New):
private ResponseEntity<ErrorResponse> funcA(Exception e) {
if (e instanceof ConversionFailedException) {
return buildResponseEntity(HttpStatus.BAD_REQUEST, e);
}
else if (e instanceof ActionNotFoundException) {
return buildResponseEntity(HttpStatus.NOT_FOUND, e);
}
// they can add any new exception here, and both the parts of the code will be able to handle it
}
Bulk API code:
try {
}catch(Exception e){
return ExceptionHandlerUtil.funcA(e);
}

Does spring boot automatically take care of error handling in the context of JpaRepository methods?

When using Spring Boot, I am unsure if error handling is already being taken care of by the Spring Framework, or if I have to implement it. For example, consider a controller method, which handles a DELETE request:
#DeleteMapping("/{studentId}")
public ResponseEntity<Long> deleteProfilePicture(#PathVariable Long studentId) {
return ResponseEntity.ok(profilePictureService.deleteprofilePictureByStudentId(studentId));
}
Is this fine, or should I instead wrap it inside a try-catch block:
#DeleteMapping("/{studentId}")
public ResponseEntity<Long> deleteProfilePicture(#PathVariable Long studentId) throws Exception {
try {
profilePictureService.deleteProfilePictureByStudentId(studentId));
} catch (DataAccessException e) {
throw new Exception("cannot delete profile picture of student: " + studentId);
}
}
Also: If I let my method deleteProfilePicture throw this Exception, who is handling the Exception? This must somehow be taken care of by Spring Boot, since it is possible to implement it without yielding any errors. Anyhow, what is the correct way of error handling in this scenario?
Spring Boot will turn the exception into an error response to the caller of the REST API. This does not mean that you shouldn't implement your own error handling logic, which you definitely should. As an example, you could use #ControllerAdvice to have a global exception handling for your application. Something along the following lines:
#ControllerAdvice
#Slf4j
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = {Exception.class})
public ResponseEntity<Object> handleGenericExceptions(Exception exception, WebRequest webRequest) {
log.error("Handling: ", exception);
HttpStatus errorCode = HttpStatus.INTERNAL_SERVER_ERROR;
return this.handleExceptionInternal(exception, new ErrorInfo(errorCode.value(), "Unexpected Internal Server Error"), new HttpHeaders(), errorCode, webRequest);
}
}
You can read more about error handling in Spring Boot at https://www.baeldung.com/exception-handling-for-rest-with-spring.

Spring 5 exception handling - ResponseStatusException model

I was reading the article - https://www.baeldung.com/exception-handling-for-rest-with-spring
which says
Spring 5 introduced the ResponseStatusException class.
We can create an instance of it providing an HttpStatus and optionally
a reason and a cause:
I started implementing it , and the code is
custom exception
#ResponseStatus(code = HttpStatus.NOT_FOUND, reason = "Actor Not Found")
public class ActorNotFoundException extends Exception {
private static final long serialVersionUID = 1L;
public ActorNotFoundException(String errorMessage) {
super(errorMessage);
}
}
method in service
public String updateActor(int index, String actorName) throws ActorNotFoundException {
if (index >= actors.size()) {
throw new ActorNotFoundException("Actor Not Found in Repsoitory");
}
actors.set(index, actorName);
return actorName;
}
controller
#GetMapping("/actor/{id}")
public String getActorName(#PathVariable("id") int id) {
try {
return actorService.getActor(id);
} catch (ActorNotFoundException ex) {
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "Actor Not Found", ex); //agreed it could be optional, but we may need original exception
}
}
repo:
https://github.com/eugenp/tutorials/tree/master/spring-5/src/main/java/com/baeldung/exception
Question:
why ResponseStatusException in controller again has to specify reason - "Actor Not Found" ?, as the service already said - ""Actor Not Found in Repsoitory"
What is the proper way to adapt to ResponseStatusException model?
It looks like a mistake. Ideally the service shouldn't use any HTTP code, so I would remove the annotation in ActorNotFoundException. Everything else seems fine, the exception is caught in the controller and ResponseStatusException is thrown which is good, because it's a proper layer to put HTTP stuff.
Overall it is better to use #ControllerAdvice instead of ResponseStatusException. it gives you a unified exception handling solution. Although it is not a good idea from a design point of view, ResponseStatusException can help you to avoid creating your custom exceptions and use it at the service level to throw in case of an Exception.
to avoid writing the message again you can use the message that is already available in thrown exception:
throw new ResponseStatusException(HttpStatus.NOT_FOUND, ex.getMessage() , ex);
for examples and more info you can refer to the following articles:
Spring Boot Exception Handling — #ControllerAdvice
Spring Boot Exception Handling — ResponseStatusException

How to pass and handle Exceptions through HTTP responses in Spring?

I have a Client and Server module in my Spring project running on separate ports. The Client module makes a POST request to the Server via a RestTemplate. The Server-Module throws a custom Exception with a custom error-message. Currently, in my Project, the Server has a RestControllerAdvice Class that handles such exceptions as follows:
#RestControllerAdvice
public class AppRestControllerAdvice {
#ExceptionHandler(ApiException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public MessageData handle(ApiException e) {
MessageData data = new MessageData();
data.setMessage(e.getMessage());
return data;
}
}
On the Client side, the following method catches the Response from the Server.
#RestControllerAdvice
public class AppRestControllerAdvice {
#ExceptionHandler(ApiException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public MessageData handle(ApiException e) {
MessageData data = new MessageData();
data.setMessage(e.getMessage());
return data;
}
#ExceptionHandler(Throwable.class)
#ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public MessageData handle(Throwable e) {
MessageData data = new MessageData();
data.setMessage("UNKNOWN ERROR- " + e.getMessage());
e.printStackTrace();
return data;
}
}
Whenever the Exception is thrown on the server, here is what I receive on the Client
{
"message": "UNKNOWN ERROR- org.springframework.web.client.HttpClientErrorException: 400 Bad Request"
}
My question is, how do I retrieve the Custom Exception message that originated on the Server?
Also, why isn't the correct RestControllerAdvice module on the Client side picking up the error? (The INTERNAL_SERVER_ERROR method catches the error instead of the BAD_REQUEST method.)
My question is, how do I retrieve the Custom Exception message that originated on the Server?
To retrieve the orignal exception message you have to use dedicated ResponseErrorHandler that is capable of extracting that information, rather than using the default one (DefaultResponseErrorHandler - which I assume you use because of the message you got - org.springframework.web.client.HttpClientErrorException: 400 Bad Request).
Create:
public class CustomerResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
// here you have access to the response's body which potentially contains the exception message you are interested in
// simply extract it if possible and throw an exception with that message
// in other case you can simply call `super.handlerError()` - do whatever suits you
}
}
Then use it with your RestTemplate:
#Configuration
public class RestTemplateConfig {
#Bean
public RestTemplate restTemplate(RestTemplateBuilder builder) {
return builder
          .errorHandler(new CustomerResponseErrorHandler())
          .build();
}
}
Also, why isn't the correct RestControllerAdvice module on the Client side picking up the error? (The INTERNAL_SERVER_ERROR method catches the error instead of the BAD_REQUEST method.)
The correct method is executed - your RestTemplate at the moment is throwing HttpClientErrorException which is not an ApiException. It is a Throwable though.

How to handle Exception occuring when returning StreamingResponseBody from RestController

I have implemented a Spring Rest Controller that streams back large files using the StreamingResponseBody. However, these files are coming from another system and there is the potential for something to go wrong while streaming them back. When this occurs I am throwing a custom Exception (MyException). I am handling the exception in an #ExceptionHandler implementation which is below. I am attempting to set the response httpstatus and error message but I am always receiving http status 406. What is the proper way to handle errors/exceptions while returning a StreamingResponseBody?
#ExceptionHandler(MyException.class)
public void handleParsException( MyException exception, HttpServletResponse response) throws IOException
{
response.sendError(HttpStatus.INTERNAL_SERVER_ERROR.value(),exception.getMessage());
}
You should handle all errors in the same way. There are many options.
I prefer next:
Controller Advice
It is a good idea to have an entity to send a generic error response, an example:
public class Error {
private String code;
private int status;
private String message;
// Getters and Setters
}
Otherwise, to handle exceptions you should create a class annotated with #ControllerAdvice and then create methods annotated with #ExceptionHandler and the exception or exceptions (it could be more than one) you want to handle. Finally return ResponseEntity<Error> with the status code you want.
public class Hanlder{
#ExceptionHandler(MyException.class)
public ResponseEntity<?> handleResourceNotFoundException(MyException
myException, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.CONFLICT.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.CONFLICT);
}
#ExceptionHandler({DataAccessException.class, , OtherException.class})
public ResponseEntity<?> handleResourceNotFoundException(Exception
exception, HttpServletRequest request) {
Error error = new Error();
error.setStatus(HttpStatus.INTERNAL_ERROR.value()); //Status you want
error.setCode("CODE");
error.setMessage(myException.getMessage());
return new ResponseEntity<>(error, null, HttpStatus.INTERNAL_ERROR);
}
}
Other ways:
Annotate exception directly
Other way is annotating directly the excetion with the status and the reason to return:
#ResponseStatus(value=HttpStatus.CONFLICT, reason="Error with StreamingResponseBody")
public class MyError extends RuntimeException {
// Impl ...
}
Exception Handler in a specific controller
Use a method annotated with #ExceptionHandler in a method of a #Controller to handle #RequestMapping exceptions:
#ResponseStatus(value=HttpStatus.CONFLICT,
reason="Error with StreamingResponse Body")
#ExceptionHandler(MyError.class)
public void entitiyExists() {
}
I figured the problem out. The client was only accepting the file type as an acceptable response. Therefore, when returning an error in the form of an html page I was getting httpstatus 406. I just needed to tell the client to accept html as well to display the message.

Resources