Exception class is overriding the AccessDeniedException - spring

I have two Exception handler. According to my logic it should fire AccessDeniedException.class exception. But it returns the Exception.class only. In the console log it fires AccessDeniedException.class exception. But in the postman response it says Internal Server which returns Exception.class exception.
Log:
Resolved [org.springframework.security.access.AccessDeniedException: Access is denied]
Exception handlers:
#ExceptionHandler(Exception.class)
public ResponseEntity<HttpResponse> internalServerErrorException() {
return createHttpResponse(INTERNAL_SERVER_ERROR, INTERNAL_SERVER_ERROR_MESSAGE);
}
#ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<HttpResponse> accessDeniedException() {
return createHttpResponse(FORBIDDEN, NOT_ENOUGH_PERMISSION);
}
Output:
{
"timeStamp": "08-26-2022 07:24:54",
"httpStatusCode": 500,
"httpStatus": "INTERNAL_SERVER_ERROR",
"reason": "INTERNAL SERVER ERROR",
"message": "AN ERROR OCCURRED WHILE PROCESSING THE REQUEST"
}

Related

Webclient onStatus does not work in case of 406 returned from downstream API

I'm doing a onStatus implementation in my API when I use a webclient (Webflux) to call external API:
//Webclient Call
Flux<Movie> movies = webclient.get().uri(uriBuilder -> uriBuilder.path(api_url)
.build(author))
.retrieve()
.onStatus(HttpStatus::is4xxClientError,
response -> Mono.error(new AcceptHeaderNotsupportedException(response.statusCode().getReasonPhrase())))
.bodyToFlux(Movie.class)
//Global Handler Exception Class
public class GlobalExceptionHandler {
#ExceptionHandler(AcceptHeaderNotsupportedException.class)
public ResponseEntity<?> AcceptHeaderHandling(AcceptHeaderNotsupportedException exception){
ApiException apiException = new ApiException(HttpStatus.NOT_FOUND.value(), exception.getMessage());
return new ResponseEntity<>(ApiException, HttpStatus.NOT_FOUND);
}
}
//AcceptHeaderNotsupportedException Class
public class AcceptHeaderNotsupportedException extends RuntimeException{
public AcceptHeaderNotsupportedException(String message){
super(message);
}
}
//Api custom Exception
public class ApiCustomException{
private int code;
private String message;
}
I am testing a scenario webclient call that return a 406 error from downstream api. So i want to map the response to my object representation and give to my client (postman in this case).
{
code: 406,
"message": error from downstream api
}
but i am getting to client
{
"timestamp": "2021-08-29T14:31:00.944+00:00",
"path": "path",
"status": 406,
"error": "Not Acceptable",
"message": "Could not find acceptable representation",
"requestId": "ba66698f-1",
"trace": "org.springframework.web.server.NotAcceptableStatusException: 406 NOT_ACCEPTABLE \"Could not find acceptable representation\"\n\tat ....}
In case of a 404 error from downstream API the mapping response works fine.
{
code: 404,
"message": not found
}
My question is if i am doing .onStatus(HttpStatus::is4xxClientError should not work for both (404, 406 or other responde code with 4xx ?

Spring Boot app deployed on cloud run removes "message" details from HTTP errors in REST

Please help me understand what's going on under the hood. I can't figure out the reason why my java spring boot app deployed on Cloud Run removes "message" details from http errors in REST API.
Here is the super simple test app that I did just to confirm nothing else affect it:
#SpringBootApplication
public class SandboxApplication {
public static void main(String[] args) {
SpringApplication.run(SandboxApplication.class, args);
}
#RestController
public class MyRestController {
#GetMapping(value = "/test", produces = MediaType.APPLICATION_JSON_VALUE)
public String testMethod() {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST, "Test Method Error Message");
}
}
}
Response I get running it locally (expected):
{
"timestamp": "2020-06-24T20:09:39.138+00:00",
"status": 400,
"error": "Bad Request",
"message": "Test Method Error Message",
"path": "/test"
}
But when I deploy it to Cloud Run and run the same app, I get it like that (see that "message" information is empty):
{
"timestamp": "2020-06-24T20:10:23.531+00:00",
"status": 400,
"error": "Bad Request",
"message": "",
"path": "/test"
}
I will keep this test app running for a while here - https://rfg-sandbox-jihqciukfq-uc.a.run.app/test

Spring Boot catch multiple exceptions and send as error response

I am validating an incoming POST request which will create a database entity after validating the request data. I am trying to gather multiple errors in a single request and respond as error response following JSON API spec:
https://jsonapi.org/examples/#error-objects-multiple-errors
HTTP/1.1 400 Bad Request
Content-Type: application/vnd.api+json
{
"errors": [
{
"status": "403",
"source": { "pointer": "/data/attributes/secretPowers" },
"detail": "Editing secret powers is not authorized on Sundays."
},
{
"status": "422",
"source": { "pointer": "/data/attributes/volume" },
"detail": "Volume does not, in fact, go to 11."
},
{
"status": "500",
"source": { "pointer": "/data/attributes/reputation" },
"title": "The backend responded with an error",
"detail": "Reputation service not responding after three requests."
}
]
}
Is it possible to do this by #ControllerAdvice. When Global exception handling is enabled by #ControllerAdvice and throws an exception, the next exception won't be caught.
Not directly, no. Not sure what is your business case/logic, therefore I don't know how you handling these exceptions in service layer, but in general, if you want to pass multiple errors in your #ExceptionHanlder - you could create a custom POJO:
public class MyError {
private String status;
private String source;
private String title;
private String detail;
getters/setters...
}
and then create a custom RuntimeException which would accept list of these POJOs:
public class MyRuntimeException extends RuntimeException {
private final List<MyError> errors;
public MyRuntimeException(List<MyError> errors) {
super();
this.errors = errors;
}
public List<MyError> getErrors() {
return errors;
}
}
And in your service layer you could create list of these POJOs, wrap then in your exception and throw it. Then in #ControllerAdvice you simply catch your exception and call accessor method to iterate against your list of POJOs to construct a payload you want.
Something like:
#ExceptionHandler (MyRuntimeException.class)
#ResponseStatus (BAD_REQUEST)
#ResponseBody
public Map<String, Object> handleMyRuntimeException(MyRuntimeException e) {
return singletonMap("errors", e.getErrors());
}

#ControllerAdvice handle the exception but not customize response

This is my exception handler
#ControllerAdvice(basePackageClasses = [SignUpController::class])
class ControllerAdvice: ResponseEntityExceptionHandler() {
#ExceptionHandler(Exception::class)
#ResponseBody
fun handleControllerException(request: HttpServletRequest, ex: Throwable): ResponseEntity<*> {
val status = HttpStatus.CONFLICT
return ResponseEntity<Any>(ApiError(status.value(), ex.message), status)
}
}
and my custom class
data class ApiError(val status: Int, val message: String?)
The handler work but error is throw like follow
{
"timestamp": "2019-01-29T19:17:22.541+0000",
"status": 401,
"error": "Unauthorized",
"message": "could not execute statement; SQL [n/a]; constraint [null]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement",
"path": "/sign-up"
}
But I'm expected for something like follow
{
"apierror": {
"status": ...,
"message": ..."
}
}
I'm based for this tutorials
https://www.toptal.com/java/spring-boot-rest-api-error-handling
https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#boot-features-error-handling
https://medium.com/#jovannypcg/understanding-springs-controlleradvice-cd96a364033f
What I'm doing wrong? I'm missing any config?
You need to annotate your ApiError class with the following:
#Data
#JsonTypeInfo(
include = JsonTypeInfo.As.WRAPPER_OBJECT,
use = JsonTypeInfo.Id.CUSTOM,
property = "error",
visible = true
)
#JsonTypeIdResolver(LowerCaseClassNameResolver.class)
My workaround was, but is not the correct answer
on application.yaml
server:
error:
include-exception: true
on my controller I'm check if email and username exists and throw a custome message to exception
#RestController
#RequestMapping("/register")
class RegisterController: BaseController() {
#ResponseStatus(HttpStatus.CREATED)
#PostMapping("/save")
fun register(#RequestBody user: ApplicationUser) {
if (userRepository.existsByEmail(user.email)) {
throw DataIntegrityViolationException("Email already exists")
}
if (userRepository.existsByUsername(user.username)) {
throw DataIntegrityViolationException("Username already exists")
}
user.password = bCryptPasswordEncoder.encode(user.password)
userRepository.save(user)
}
}
and the return worked fine
{
"timestamp": "2019-01-31T17:08:40.832+0000",
"status": 500,
"error": "Internal Server Error",
"exception": "org.springframework.dao.DataIntegrityViolationException",
"message": "Email already exists", //here is
"path": "/register/save"
}

Spring boot + REST exception Handler - always get 500 error

I have a REST service which could throw an exception.
This is my custom exception
public class CommentNotPostableException extends Exception {
public CommentNotPostableException(final String message) {
super(message);
}
}
Then, for my REST Api, I implemented a RestResponseEntityExceptionHandler which extends ResponseEntityExceptionHandler
One of its methods is
#ExceptionHandler(value = { CommentNotPostableException.class })
protected ResponseEntity<Object> handleCommentNotPostableException(CommentNotPostableException ex, WebRequest request) {
StringBuilder builder = new StringBuilder();
builder.append(ex.getMessage());
ApiError apiError = new ApiError(HttpStatus.valueOf(46),
ex.getLocalizedMessage(), builder.substring(0, builder.length()));
logger.error("Already posted", ex);
return new ResponseEntity<Object>(apiError, new HttpHeaders(), apiError.getStatus());
}
which should get the exception...
Now my controller is (snippet)
public ResponseEntity<?> postComment(#Valid #RequestBody CommentDTO dto, Errors errors) throws CommentNotPostableException{
.....
if(service.hasAlreadyPosted(user, reservation)){
throw new CommentNotPostableException("Already posted");
}
....
}
So, when hitting the exception i should recevive an error 46, instead i'm getting a 500 error, even if my custom exception is taken into account... Is there some kind of ordering in exceptions?
{
"timestamp": 1496084392755,
"status": 500,
"error": "Internal Server Error",
"exception": "it.besmart.easyparking.exceptions.CommentNotPostableException",
"message": "Already posted",
"path": "/api/oauth/comment/new"
}
this is what i get from logs
2017-05-29 21:13:32 DEBUG i.b.e.w.r.CommentRestController[57] - dto รจ CommentDTO [comment=A, vote=3, reservationId=7161]
2017-05-29 21:13:32 ERROR o.a.c.c.C.[.[.[.[dispatcherServlet][181] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is it.besmart.easyparking.exceptions.CommentNotPostableException: Already posted] with root cause
it.besmart.easyparking.exceptions.CommentNotPostableException: Already posted
at it.besmart.easyparking.web.restcontroller.CommentRestController.postComment(CommentRestController.java:78) ~[classes/:na]

Resources