Spring boot translate default exceptions - java

1 . What i want to do is set default message for #Size validation like this
#Size(min = 8,max = 255,message = "{validation.size}")
I dont want to put this on every field, so im asking if there is any way of setting global translation of this message..
I want to translate error messages that are comming from spring security like Forbidden, Access denied etc
I already tried putting spring security messages like these in messages.properties
ExceptionTranslationFilter.insufficientAuthentication=A Custom message
AbstractAccessDecisionManager.accessDenied=A Custom message
But doesnt seem to be working..

You'll need a class annotated with #RestControllerAdvice. Add methods there to capture relevant exceptions and you can translate them into your responses. Even mess with http status response codes too if you want.
#RestControllerAdvice
#Slf4j
public class MyControllerExceptionHandling {
#ExceptionHandler(MethodArgumentNotValidException.class)
#ResponseStatus(BAD_REQUEST)
public MyErrorMessageDto handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
return MyErrorMessageDto
.builder()
.cause("Bad something something")
.build();
}
}
Look up exceptions like MethodArgumentNotValidException and ConstraintViolationException as you'll need to handle these (and others) depending on your controller validation.
NB: The JSON you return for errors is just as much a contract with your clients as the JSON you return for successes, so have a google and see what others have done (before you get locked into something you wish you'd done better!).

Related

Spring Boot how to return custom error message when validating #PathVariable parameters

Is it possible to add some custom validation message to path variable?
I have some GET
#GetMapping("/v2/tw/{id}")
public TwDto getTw(Authentication auth, #PathVariable Long id) {
In case of /v2/tw/someString I'd like to catch error and add some custom error message like "invalid tw ID"... How to do that? In ControllerAdvice add some ExceptionHandler?
For your particular use case, you can use #ExceptionHandler in the Controller or in a #ControllerAdvice class as shown here. For example, I am returning NOT_FOUND error for the sake of it.
#ExceptionHandler({MethodArgumentTypeMismatchException.class})
#ResponseStatus(value = HttpStatus.NOT_FOUND, reason = "this is the reason")
public void handle() {
}
You may not see the reason in the actual error response, until you enable
server:
error:
include-message: always
If you think your #ExceptionHandler is only needed in a Controller class you can keep the method inside the controller. Alternatively you can create a #ControllerAdvice class and put the method there, so that you can reuse across multiple controllers in your application.
However, if you want a more complex validation, I will suggest to keep the id type to String and then cast manually into Long and perform the validation. Doing so you can throw your own RuntimeException and handle different cases.

How do I change only the status code on a Spring MVC error with Boot?

I'm writing a Web application that makes downstream calls using RestTemplate. If the underlying service returns a 401 Unauthorized, I want to also return a 401 to the calling application; the default behavior is to return a 500. I want to keep the default Spring Boot error response as provided by BasicErrorController; the only change I want is to set the status code.
In custom exceptions, I'd just annotate the exception class with #ResponseStatus, but I can't do that here because HttpClientErrorException.Unauthorized is provided by Spring. I tried two approaches with #ControllerAdvice:
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void returnsEmptyBody(HttpClientErrorException.Unauthorized ex) {
}
#ExceptionHandler(HttpClientErrorException.Unauthorized.class)
#ResponseStatus(UNAUTHORIZED)
public void doesNotUseBasicErrorController(HttpClientErrorException.Unauthorized ex) {
throw new RuntimeException(ex);
}
How can I configure MVC to continue to use all of the built-in Boot error handling except for explicitly overriding the status code?
The below code works for me -- in an app consisting of a #RestController whose one method consisted of throw new HttpClientException(HttpStatus.UNAUTHORIZED), running on an embedded Tomcat. If you're running on a non-embedded Tomcat (or, I suspect, on an embedded non-Tomcat) odds are you'll have to do something at least somewhat different, but I hope this answer is at least somewhat helpful anyway.
#ControllerAdvice
public class Advisor {
#ExceptionHandler(HttpClientException.class)
public String handleUnauthorizedFromApi(HttpClientException ex, HttpServletRequest req) {
if (/* ex instanceof HttpClientException.Unauthorized or whatever */) {
req.setAttribute(RequestDispatcher.ERROR_STATUS_CODE, 401);
}
return "forward:/error";
}
}
Explanation: when a HttpClientException is thrown while we're processing request X (in an embedded servlet), what normally happens is that it bubbles all the way up to some org.apache class. (I might fire the debugger up again and work out which one, but this is a pretty high-level explanation so it doesn't matter much.) That class then sends request X back to the application, except this time the request goes to "/error", not to wherever it was originally going. In a Spring Boot app (as long as you don't turn some autoconfiguration off), that means that request X is ultimately processed by some method in BasicErrorController.
OK, so why does this whole system send a 500 to the client unless we do something? Because that org.apache class mentioned above sets something on request X which says "processing this went wrong". It is right to do so: processing request X did, after all, result in an exception which the servlet container had to catch. As far as the container is concerned, the app messed up.
So we want to do a couple of things. First, we want the servlet container to not think we messed up. We achieve this by telling Spring to catch the exception before it reaches the container, ie by writing an #ExceptionHandler method. Second, we want the request to go to "/error" even though we caught the exception. We achieve this by the simple method of sending it there ourselves, via a forward. Third, we want the BasicErrorController to set the correct status and message on the response it sends. It turns out that BasicErrorController (working in tandem with its immediate superclass) looks at an attribute on the request to determine what status code to send to the client. (Figuring this out requires reading the class's source code, but that source code is on github and perfectly readable.) We therefore set that attribute.
EDIT: I got a bit carried away writing this and forgot to mention that I don't think using this code is good practice. It ties you to some implementation details of BasicErrorController, and it's just not the way that the Boot classes are expected to be used. Spring Boot generally assumes that you want it to handle your error completely or not at all; this is a reasonable assumption, too, since piecemeal error handling is generally not a great idea. My recommendation to you -- even if the code above (or something like it) does wind up working -- is to write an #ExceptionHandler that handles the error completely, meaning it sets both status and response body and doesn't forward to anything.
You can customize the error handler of the RestTemplate to throw your custom exception, and then handle that exception with the #ControllerAdvice as you mentioned.
Something like this:
#Configuration
public class RestConfig {
#Bean
public RestTemplate restTemplate(){
// Build rest template
RestTemplate res = new RestTemplate();
res.setErrorHandler(new MyResponseErrorHandler());
return res;
}
private class MyResponseErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (HttpStatus.UNAUTHORIZED.equals(response.getStatusCode())) {
// Throw your custom exception here
}
}
}
}

Production level Exception handling in spring boot

I have a scenario :
UI<--->Spring boot micro-service REST API<--->server
Now, there is a situation in which I want to handle custom exceptions(which I am aware how to do) in order to return specific Http Status and message back to UI when server responds in certain manner for e.g. 500 should return "Please return after a while" insteal of "internal server error". The maven project for our micro-service is divided into 3 layers (sub maven projects) i.e. Business, Web and Domain. Where web contains controller class, Business contains Service class and Domain contains #Entity, #Components etc.
I want to know in order to handle the above mentioned exception let say HTTP Status 500, should be done at business layer? or at web layer i.e. controller level. What would be the best possible solution? (I am aware of ResponseEntity and how it can provide a customized response to UI).
I personally believe that if I include custom exception class at Business Level and return that in Controller class using response entity after checking the response status would do the trick. But officials feel that it should be done at service level? I am not able to understand why(it makes the process more complex)? can anyone suggest which solution is best?
Officials are right, it should be in Service Layer. I would say best practice would be to use #ExceptionHandler. As the downside to handling the exception in the controller method is that it makes the code less readable and might be repeated across many controller methods.
I would recommend having a base class for your controllers with the #ExceptionHandler defined. This way it can be used for many different controllers, without any code duplication. This would be more readable than the exception resolver approach, but could be used in conjunction
This is clearly explained here
If you want to handle error in a global level, you can use #ControllerAdvice which is very easy when comes to the handling custom exceptions as well as runtime exceptions.
you can throw exceptions from business layer to web controllers and define a #ControllerAdvice class to catch those errors and provide responses with correct response status.
For Ex:-
#ControllerAdvice
public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = { IllegalArgumentException.class, IllegalStateException.class })
protected ResponseEntity<Object> handleConflict(RuntimeException ex, WebRequest request) {
String bodyOfResponse = "This should be application specific";
return handleExceptionInternal(ex, bodyOfResponse,
new HttpHeaders(), HttpStatus.CONFLICT, request);
}
}
and throw those Exception classes from the controller classes also, you dont need to catch exception from the controllers.
Hope this helps to you...
Above code snipplet i took from here
Error response is usually generated by #ExceptionHandler matching your exception type and maybe registered with #ConrtrollerAdvice as described here.
API should be standardised (e.g. http://jsonapi.org/) and designed primarily for developers. Returning "Please return after a while" instead of "Internal Server Error" makes little sense to me. It's a 500 HTTP status response of uncertain reason e.g. NullPointerException somewhere deep in the code.

How to handle the GET method so that I can update the requested object before returning it?

I'm working with an API implemented with Spring Data Rest and I would need to handle the GET method like I can do with the #RepositoryEventHandler, which allows me to handle before/after save, create, delete...
The case is that I need to update the object to be returned gathering information from other APIs before returning it as the GET response.
Is there any way to handle the GET in spring data rest?
You probably need to use #RepositoryRestController for this. It lets you get stuff from the #Repository and then add new things to the response object. Pretty similar to #RestController, but keeps Spring Data REST’s settings, message converters, exception handling, and more.
you could add Interceptor implementing HandleInterceptor and add it to mapped inteceptor bean.
#Bean
public MappedInterceptor myMappedInterceptor() {
return new MappedInterceptor(new String[]{"/**"}, new MyInterceptor());
}

How can I see if the request path matched but Accept did not in a catch-all handler?

Normally spring will return a 404 response for a request mapping where the path did not match, and return a 406 if the path did match but the "Accept" header did not match.
I have a default controller which acts as a "catch-all" which handles rest faults by returning faults in the Accepted format. Controller is of the form:
#Controller
public class DefaultController {
#RequestMapping("/**")
public void unmappedRequest(HtpServletRequest req) {
throw new ResourceNotFoundException();
}
}
Trouble is if I do get a match here, I can't tell if it matched elsewhere. I want to return the correct error to the client and tell them the Acceptable types they may retry with. Currently all I can do is throw a general ResourceNotFound exception.
Is this something I can do in a #Controller or am I going to need to resort to writing some sort of filter chain for this?
FWIW I'm using the ReST exception handling pattern demonstrated by Stormpath
I don't think a catch all controller is good way to handle "unmapped urls" at all.
I would suggest implementing a custom implementation of AbstractHandlerExceptionResolver, and not relying on the Default implementation provided by Spring. The doResolveException can be extended to do almost anything you want with the request and response.
If you want this custom ExceptionResolver to apply only to specific controllers (REST controllers), you can set mappedHandlerClasses on the exception resolver with a list of controllers you want. Also, you can set the order of the custom exception resolver such that it sits before the default resolver.
If you think I'm off topic here, please let me know.

Categories