Accessing the error object inside of exceptionhandler method - java

Is there any way to get inside to the exception object when using annotation #ExceptionHandler?
this is my code:
#ExceptionHandler(DataInputException.class)
public ResponseEntity handleException(){
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body("Entity contains null or forbidden values");
}
I'd like to have returned message to contain customized info about perticular fields. (That's why I need error object).

Pass in the exception
#ExceptionHandler(DataInputException.class)
public ResponseEntity handleException(DataInputException exception) {

Related

How can I access the request object when handling a MethodArgumentNotValidException?

I'm handling a MethodArgumentNotValidException thrown after a failed validation of a request object. All the usual stuff is in place: #Valid, #ControllerAdvice, and an extended ResponseEntityExceptionHandler, in which I override handleMethodArgumentNotValid().
As it happens, I need to access that same request object in order to form a customized error response. One way would be to intercept the request before it hits the controller and create a #RequestScope bean with the needed fields in case validation fails later.
Is there a better way?
Thanks to a suggestion from a colleague, I've found that the BindingResult within MethodArgumentNotValidException has a method named getTarget() that returns the validated object. As seen from the method signature (Object getTarget()), the return value needs a cast.
You should have the error fields in the MethodArgumentNotValidException class. Your handleMethodArgumentNotValid function might look like something as follows.
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
#ResponseBody
public CustomInputErrorResponse handleMethodArgumentNotValid(MethodArgumentNotValidException e) {
String message = "Invalid inputs";
ArrayList<String> fieldNames = new ArrayList<String>();
for (FieldError fieldError : e.getBindingResult().getFieldErrors()) {
fieldNames.add(fieldError.getField());
}
return new CustomInputErrorResponse(message, fieldNames);
}
Considering you have a CustomInputErrorResponse class that takes two arguments for a custom message and error field names.

Spring Reactive WebFlux - how to customize the BadRequest error message

In my request handler, if the passed-in accountId cannot be converted to a valid ObjectId I want to catch the error and send back a meaningful message; however, doing so causes the return type to be incompatible, and I cannot figure out how to achieve this pretty trivial use case.
My code:
#GetMapping("/{accountId}")
public Mono<ResponseEntity<Account>> get(#PathVariable String accountId) {
log.debug(GETTING_DATA_FOR_ACCOUNT, accountId);
try {
ObjectId id = new ObjectId(accountId);
return repository.findById(id)
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
} catch (IllegalArgumentException ex) {
log.error(MALFORMED_OBJECT_ID, accountId);
// TODO(marco): find a way to return the custom error message. This seems to be currently
// impossible with the Reactive API, as using body(message) changes the return type to
// be incompatible (and Mono<ResponseEntity<?>> does not seem to cut it).
return Mono.just(ResponseEntity.badRequest().build());
}
}
The body(T body) method changes the type of the returned Mono so that it is (assuming one just sends a String) a Mono<ResponseEntity<String>>; however, changing the method's return type to Mono<ResponseEntity<?>> does not work either:
...
return Mono.just(ResponseEntity.badRequest().body(
MALFORMED_OBJECT_ID.replace("{}", accountId)));
as it gives an "incompatible type" error on the other return statement:
error: incompatible types: Mono<ResponseEntity<Account>> cannot be converted to Mono<ResponseEntity<?>>
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
Obviously, changing the return type of the method to Mono<?> would work, but the response then is the serialized JSON of the ResponseEntity which is NOT what I want.
I have also tried using the onErrorXxxx() methods, but they do not work here either, as the conversion error happens even before the Flux is computed, and I just get a "vanilla" 400 error with an empty message.
The only way I can think of working around this would be to add a message field to my Account object and return that one, but it's genuinely a horrible hack.
#thomas-andolf's answer helped me figure out the actual solution.
For anyone stumbling upon this in future, here is how I actually solved the puzzle (and, yes, you still need the try/catch to intercept the error thrown by the ObjectId constructor):
#GetMapping("/{accountId}")
public Mono<ResponseEntity<Account>> get(#PathVariable String accountId) {
return Mono.just(accountId)
.map(acctId -> {
try {
return new ObjectId(accountId);
} catch (IllegalArgumentException ex) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST,
MALFORMED_OBJECT_ID));
}
})
.flatMap(repository::findById)
.map(ResponseEntity::ok)
.switchIfEmpty(Mono.just(ResponseEntity.notFound().build()));
}
To actually see the message in the returned body, you will need to add server.error.include-message=always in application.properties (see here).
Using onError() won't work here (I did try that, in all its variants) as it requires a Mono<ResponseEntity<Account>> and there is no way to generate one from the error status (when adding the message body).

How to properly throw MethodArgumentNotValidException

I'm trying to get the same result as when I use #Valid in object parameter from a Controller. When the object is invalid an exception (MethodArgumentNotValidException) is throw by my ExceptionHandlerController who has #RestControllerAdvice.
In my case I want to validate an object, but I only can validate it in service layer. The object have bean validation annotations, so I'm trying to programmatically throw MethodArgumentNotValidException for my ExceptionHandlerController handle it, but I'm not having success.
So far I have this:
private void verifyCard(CardRequest card) {
BeanPropertyBindingResult result = new BeanPropertyBindingResult(card, "card");
SpringValidatorAdapter adapter = new SpringValidatorAdapter(this.validator);
adapter.validate(card, result);
if (result.hasErrors()) {
try {
throw new MethodArgumentNotValidException(null, result);
} catch (MethodArgumentNotValidException e) {
e.printStackTrace();
}
}
}
The first parameter is from type MethodParameter and I'm not been able to create this object. Is it the best way to handle my problem?
EDIT 1:
I can't remove the try/catch block. When I remove it I get compile error. How to work around?
You have already handled it by the catch block, you should remove try-catch to your global handler catch it.
then specify the method like below
private void verifyCard(CardRequest card) throws MethodArgumentNotValidException
MethodArgumentNotValidException is a subclass of Exception. This means that it's "checked": To throw it out of your verifyCard(..) method, you have to declare that verifyCard(..) can throw it:
private void verifyCard(CardRequest card) throws MethodArgumentNotValidException {
// your code
}
If you have lombok dependency in your project, you can also fake compiler by using #SneakyThrows annotation.
https://projectlombok.org/features/SneakyThrows
throw new MethodArgumentNotValidException(null, result);
Above constructor will not work as method parameter is necessary.
Valid constructor (reference) is:
MethodArgumentNotValidException(MethodParameter parameter, BindingResult bindingResult);
Hence, in your case:
throw new MethodArgumentNotValidException(new MethodParameter(
this.getClass().getDeclaredMethod("verifyCard", YourClassName.class), 0), errors);

Deserializing null into a specific object in gson

I have this old implementation of awebservice class, which works off of retrofit1 and rxjava1 and is just a pain to work with, so wrote a new one from scratch.
Every response to an API-request is enveloped with the following object
{
"status":200,
"payload": {}
}
I bypassed the envelope-problem by simply wrapping every response into a generic object and when accessing the payload, I can do that easily by calling the getter
class BaseResponse<T> {
int status;
T payload;
public T getPayload(){
return payload;
}
}
The idea is to turn this BaseResponse<T> into Observable<T> and then call map {it.payload} to work with in some other place
interface API {
#Body("blablabla")
Observable<BaseResponse<SomeResponse>> someCall(#Body SomeRequest request);
}
public Observable<SomeResponse> someCall(SomeRequest request){
return api.someCall(request)
.map( response -> response.payload)
}
Now I've run into a problem. There are some API-requests, where the responsebody is defined as empty. I defined these empty responsebodies as class EmptyResponse {}
The problem is that I get those empty bodies like this
{
"status":200,
"payload":null
}
Now the problem is that payload is null and rxjava2 doesn't want null values.
Is there some way to have GSON deserialize null into an object of type EmptyResponse? Note that this ONLY occurs for calls that are documented to return empty responsebodies, there is no way that a call would return one, if it had a documented body

ExceptionHandler invokes but doesn't affect http response

I have the following method:
#ExceptionHandler(InvalidPriceUpdateException.class)
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleInvalidPriceUpdateException(InvalidPriceUpdateException e) throws JsonProcessingException {
return objectMapper.writeValueAsString(new HttpErrorDTO(e.getMessage()));
}
I see(in debug) that it invokes but in browser I see 500 error(instead of HttpStatus.BAD_REQUEST). and content of http response contains exception info(instead of HttpErrorDTO structure).
What wrong in my code?
It's because you are returning a String, where you should really be building the entire ResponseEntity. Have a read here on what that means and how to build one for example.

Categories