In my experience, limited though it may be, I have only ever seen the ExceptionHandler class used to immediately return exceptions. I know this is the purpose of an ExceptionHandler class but this brings me to my question: If a request fails validation, is it possible for the ExceptionHandler class to "fix" the request body and re-run the request?
As an example, given the following object:
public class Person {
#Pattern(regexp = "[A-Za-z]")
private String firstName;
}
Could the following Handler class:
#ExceptionHandler(ParameterNotValidException.class)
public Map<String, String> handleValidationExceptions(
ParameterNotValidException ex) {
Map<String, String> errors = new HashMap<>();
ex.getBindingResult().getAllErrors().forEach((error) -> {
String fieldName = ((FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
errors.put(fieldName, errorMessage);
});
return errors;
}
Be modified like this:
#ExceptionHandler(ParameterNotValidException.class)
public void handleValidationExceptions(String requestBody) {
requestBody = removeSpecialCharacters(requestBody);
try {
personController.putPerson(requestBody);
} catch (Exception e) {
//fail gracefully
}
}
My apologies in advance, this is my first question on StackOverflow.
It is not acceptable. ExceptionHandler is a common place where we can manage and handle exceptions and send the respective error code for the API response.
See documentation.
It is designed for:
Handle exceptions without the #ResponseStatus annotation (typically predefined exceptions that you didn’t write)
Redirect the user to a dedicated error view
Build a totally custom error response
In your case special characters should be handled at json serialisation\deserialisation stage. Escape JSON string in Java
Related
I am adding a new record in the Postman using the URI localhost:8080//insurance/service/add.
Requirement - I want the application to throw an error in JSON mentioning an error code and customized message if any non-whitespace characters is after the URI. For example, if I want to add a record by using the URI such as localhost:8080//insurance/service/add? or localhost:8080//insurance/service/add* or anything like that, it should throw an error in JSON mentioning the error code and message. How should I proceed?
PS - new with spring boot.
#RestController
#RequestMapping("insurance/service")
public class InsuranceController{
#Autowired
Insurance_Service service;
// Create New Insurance
#PostMapping(path="/add", produces = "application/json")
public String addInsurance(#RequestBody (required=false) Insurance insurance ) {
if(insurance==null)
throw new MissingQueryParam();
this.service.addInsurances(insurance);
return "Insurance added successfully!!!";
}
}
You can use #RestControllerAdvice or #ControllerAdvice to properly handle exceptions with http status.
#RestControllerAdvice
public class WebRestControllerAdvice {
#ExceptionHandler(RuntimeException.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
public ResponseMsg handleNotFoundException(Throwable ex) {
ResponseMsg responseMsg = new ResponseMsg(ex.getMessage());
return responseMsg;
}
}
ResponseMsg is coustomised Class to generate customised error response.
In this class you can handle any exception (customized too)
I need to serve multiple file types using same endpoint ( zip,pdf,xml).
I needed to add error handling to those endpoints so in case of error they should return json (using controller advice) to indicate problem to user.
For example:
#GetMapping(value = "api/books", produces = {applicaton/zip, application/json}
public ResponseEntity<byte[]> getZipedBooks(){...}
#GetMapping(value = "api/books", produces = {applicaton/pdf, application/json}
public ResponseEntity<byte[]> getPdfBooks()(...}
Without application/json Spring was able to differentiate between those endpoints and call correct one based on accept header. But when I added json Spring is now throwing exception:
org.springframework.web.HttpMediaTypeNotAcceptableException: Could not find acceptable representation
even if it can be deduced from accept: application/json,application/pdf header that getPdfBooks should be called.
Is there any way to configure spring to work with multiple content types on the same endpoint or I need to make special endpoints for every file type ?
I would reconsider this approach. If you want to return JSON on error do with exception handling
So that you would add something like
private class ErrorResponse {
String message;
public ErrorResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}
#ExceptionHandler(value = Exception.class)
public ResponseEntity<ErrorResponse> handleException(Exception e) {
return new ResponseEntity<ErrorResponse>(new ErrorResponse(e.getMessage()), HttpStatus.BAD_REQUEST);
}
I have a Spring Boot application that is running Netty for a REST API.
I have a WebExceptionHandler to handle exceptions that may occur, which is working. It builds appropriate responses and sends them.
The problem is, it also still logs the exception as an error. I want to change this to log it as an info instead (because we have tracking and alerts that operate differently based on info or error). It even logs things like 404's as errors.
It seems like exceptionCaught in a ChannelInboundHandler would help, but exceptionCaught is deprecated. I can't find anything that doesn't use this method, and I can't find anything referring to what (if anything) has replaced it).
I also tried using an #ControllerAdvice or #RestControllerAdvice with an #ExceptionHandler, but that is never called.
What is the correct way to intercept and handle the logging of the exception?
A minimal example implementation of our current set-up looks like this:
#RestController
class MyController {
#RequestMapping(method = [GET], value = ["endpoint"], produces = [APPLICATION_JSON_VALUE])
fun myEndpoint(): Mono<MyResponse> = createResponse()
}
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
class MyWebExceptionHandler : WebExceptionHandler {
// This does get called and sends the appropriate response back, but an error is logged somewhere outside of our code.
override fun handle(exchange: ServerWebExchange, ex: Throwable): Mono<Void> =
createErrorResponse();
}
// Tried using both, or just one at a time, no difference.
// It does get created.
#ControllerAdvice
#RestControllerAdvice
class MyExceptionHandler {
// Never called
#ExceptionHandler(Exception::class)
fun handle(ex: Exception) {
log.error(ex.message)
}
}
More information on how you are implementing your Exception Handler would be helpful.
here's a simple implementation which i follow to convert the exceptions and log them.
#ControllerAdvice
public class DefaultExceptionHandler extends ResponseEntityExceptionHandler {
private static final Logger LOG = LoggerFactory.getLogger(DefaultExceptionHandler.class);
private static String ERROR = "ERROR";
#ExceptionHandler(Exception.class)
ResponseEntity<Map<String, Map<String, String>>> exception(Exception e) {
Map<String,Map<String,String>> map = new HashMap<>(1);
Map<String,String> m = new HashMap<>(1);
m.put("message",e.getMessage());
map.put(ERROR, m);
LOG.info("some error " + e);
return new ResponseEntity<>(map, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
Also don't forget to create a bean or add you ExceptionHandler class to the spring config.
#Bean
public DefaultExceptionHandler defaultExceptionHandler(){
return new DefaultExceptionHandler();
}
Using latest Spring Boot as of May 2018. I've created a 404 response like this.
#ResponseStatus(HttpStatus.NOT_FOUND)
public class NotFoundException extends RuntimeException {
private final int errorId;
public NotFoundException(String errorMsg) {
super("-1," + errorMsg);
this.errorId = -1;
}
public NotFoundException(int errorId, String errorMsg) {
super(errorId + "," + errorMsg);
this.errorId = errorId;
}
public int getErrorId() {
return errorId;
}
}
The annotation #ResponseStatus(HttpStatus.NOT_FOUND) makes my NotFoundException appear like a 404 reponse like this
{
"timestamp":1527751944754,
"status":404,
"error":"Not Found",
"exception":"com.myapp.exception.NotFoundException",
"message":"1000,Could not find data for owner: 1234","path":"/resource/owner/1234"
}
I hoped that property "getErrorId" would appear in the response automatically, like this
{
"timestamp":1527751944754,
"status":404,
"error":"Not Found",
"exception":"com.myapp.exception.NotFoundException",
"message":"Could not find data for owner: 1234","path":"/resource/owner/1234",
"errorId": 1000
}
Is the a simply way (like an annotiation to the getErrorId method) of having the property "errorId" in the response?
You use #ControllerAdvice and #ExceptionHanlder in Spring. that is exception controller. In fact, you will make custom exception controller and define exception.
This is sample code for you :
#ControllerAdvice("your.package")
public class CommonExceptionHandler {
#ExceptionHandler(value = NoHandlerFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
public #ResponseBody ResponseEntity<?> setNotFoundException(Exception exception) throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// this is sample map. you will make your custom model and you use exception parameter.
Map<String, String> map = new HashMap<String, String>();
map.put("timestamp", String.valueOf(new Date().getTime()));
map.put("status", HttpStatus.NOT_FOUND.toString());
map.put("error", "Not Found");
map.put("exception", exception.getMessage());
map.put("message", "Could not find data for owner: 1234");
map.put("path", "/resource/owner/1234");
map.put("errorId", "1000");
String json = mapper.writeValueAsString(map);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(json);
}
}
what ever Byeon0gam told everything is fine, here i am going to show another way means little bit of difference in maintaining code.
We know already ,
we can handle exceptions in spring-rest by 4 ways:
1. Using ResponseEntity Class.
2. Using #ResponseStatus Annotation.
3. Using #ExceptionHandler() Annotation.
4. Return Error Representation instead of default HTML error Page.
By using Those we can handle Exceptions at Method or Class level only.
But, if you want to handle Globally means throughout application , please follow below steps.
Handling Global Exception:
To Handle all Exceptions in our applications ,
First we need to create a class, after we need to use #ControllerAdvice Annotation on top of a class. In that class body , we can handle the exceptions raised in our application.
In that Class , we will create Exception handling methods , on top of every method we will use #ExceptionHandler() annotation for navigating Exceptions and for Handling .
If any exception raises in our application , based on #ExceptionHandler(“argument”) annotation argument the exception hadling method will be invoked and remaining handling code will be excuted.
#ControllerAdvice
public class SpringRestGlobalExceptionHandler {
#ExceptionHandler(Exception.class)
public ResponseEntity<?> exceptionHandler(HttpServletRequest req, Exception e)
{
JSONObject obj =new JSONObject();
obj.put("msgTxt","Unknown Server Error, Please Contact Admin." );
obj.put("reqUrl", req.getRequestURI());
obj.put("stackTrace", e.toString());
obj.put("isErrorFlag", true);
obj.put("httpStatusCode", HttpStatus.OK.value());
gstcDaoi.saveExceptionOrErrorLog(prepareTGstcExceptionOrErrorLogObject(obj));
e.printStackTrace();
return new ResponseEntity<>(obj, HttpStatus.OK);
}
I am using Spring Boot 1.5.9 for developing my application. I need implement jwt authentication, and I used jjwt library. The following code is from my custom authentication security filter which inherits from OncePerRequestFilter. Here I tried to parse the username from token, when username is parsing automatically is jwt verified and also check expiration of token. I debug it and it works, so I next want to send the correct message to the client app why authentication failed. I want to throw an ExpiredJwtException and handle it with the controller advice where I format the output.
Here is exception throwing:
try {
username = jwtTokenService.getUsername(authToken);
} catch (IllegalArgumentException e) {
logger.error("an error occured during getting username from token", e);
} catch (ExpiredJwtException e) {
logger.warn("the token is expired and not valid anymore", e);
throw new ExpiredJwtException(e.getHeader(), e.getClaims(), e.getMessage());
}
And here is my controller Advice, JwtException is base class of ExpiredJwtException which I throw so it should work. I also tried directly use ExpiredJwtException in ExceptionHandler, but didn't work as well. Next I want to handle another exceptions with same way.
#ControllerAdvice
public class GlobalControllerExceptionHandler {
#ExceptionHandler(Exception.class)
public #ResponseBody
ResponseEntity<Map<String, Object>> handleException(Exception ex) {
Map<String, Object> errorInfo = new HashMap<>();
errorInfo.put("message", ex.getMessage());
errorInfo.put("status", HttpStatus.BAD_REQUEST);
errorInfo.put("status_code", HttpStatus.BAD_REQUEST.value());
return new ResponseEntity<>(errorInfo, HttpStatus.BAD_REQUEST);
}
#ExceptionHandler(JwtException.class)
//#ResponseStatus(HttpStatus.UNPROCESSABLE_ENTITY)
public #ResponseBody
ResponseEntity handleJwtException(JwtException ex) {
Map<String, Object> errorInfo = new HashMap<>();
errorInfo.put("message", ex.getLocalizedMessage());
errorInfo.put("status", HttpStatus.UNPROCESSABLE_ENTITY);
errorInfo.put("status_code", HttpStatus.UNPROCESSABLE_ENTITY.value());
return new ResponseEntity<>(errorInfo, HttpStatus.UNPROCESSABLE_ENTITY);
}
}
Here is my folder structure:
I want return just response with 4xx status, but I always got 5xx Internal error when my exception is thrown. Can you tell me what is wrong with my code? Thanks in advice.
If the exception is thrown in filter, Springs exception handling (#ControllerAdvice, #ExceptionHandler) is not involved.
You need to catch all exceptions inside filter and work directly with ServletResponse.
As I understand - Filters are low level logic (request handling before spring infrastructure), but you can have a workaround, like a specific filter that wraps chaining and catches all RuntimeExceptions. (Looks like a crunch, but no other solutions).
If you want to have a specific login to create your exception object - override ErrorAttributes bean. It will allow you to have a single view for all application exceptions.
To directly specify http response status usehttpServletResponse.setStatus(... your status code ...);
Have your controller extend ResponseEntityExceptionHandler and have your exception handling methods take in the WebRequest as a parameter
Then change your return value to this
return handleExceptionInternal(ex, errorInfo, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
The HttpStatus.BAD_REQUEST can be changed to any 40x error
Example for Exception.class
#ExceptionHandler(value = { Exception.class })
protected ResponseEntity<Object> handleUncaughtException(Exception ex, WebRequest request) {
String message = "Something bad happened";
return handleExceptionInternal(ex, message, new HttpHeaders(), HttpStatus.BAD_REQUEST, request);
}
According to this Make simple servlet filter work with #ControllerAdvice you can create a custom handler.
Then add your new handler to your WebSecurityConfigurerAdapter
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new CustomHandler());
}
I also faced this issue in which RestControllerAdivce was not handling the exception, Thing is that advice method can have only those arguments in its signature which exception throwing method have or can provide. My AOP method was not having access to Headers so it could not provide Headers to RestControllerAdivce method. As soon as I created a new exception handler method in RestController without Headers as argument, RestControllerAdivce started working as expected. Detials here