help me anybody Please in this issue.
The project, I am working on is old mvc, and is not going to be change to rest, So have to deal with "what we have :) ".
this is my controller method, the class of which is anotated #Controller
#RequestMapping(method=RequestMethod.POST)
public String createSomething(#RequestBody somejson, Model m) throws Exception {
SomeCustomListenerClass listener = new SomeCustomListenerClass(m);
AnnotherClass ac = somejson.toNotification(someService, anotherService, listener);
try {
ac = someService.createSomething(ac, listener);
m.addAttribute("success", true);
m.addAttribute("notificationId", ac.getId());
}
catch(SawtoothException ex) {
return handleError(ex, "Create Notification", listener);
}
return "structured";
}
and this one is handleError method body
private String handleError(Exception ex, String operation, SomeCustomListenerClass listener) {
if (!listener.hasErrors()) {
log.error("Unexpected error getting notification detail", ex);
listener.error("notification.controllerException", operation);
}
return "error";
}
Now I am getting the right errors in the client side, say in browser, but also getting the status code 500
now my boss says that we have to get 400, when validation errors hapens, not 500, as is now.
So, Please help me guys, how to overcome to this problem.
You can extend your exceptions and throw them on your controller:
#ResponseStatus(value=HttpStatus.BAD_REQUEST, reason="Your exception message")
public class YourCustomException extends RuntimeException {
}
Or you can use an ExceptionControllerHandler:
#Controller
public class ExceptionHandlingController {
// #RequestHandler methods
...
// Exception handling methods
// Convert a predefined exception to an HTTP Status code
#ResponseStatus(value=HttpStatus.CONFLICT,
reason="Data integrity violation") // 409
#ExceptionHandler(DataIntegrityViolationException.class)
public void conflict() {
// Nothing to do
}
// Specify name of a specific view that will be used to display the error:
#ExceptionHandler({SQLException.class,DataAccessException.class})
public String databaseError() {
// Nothing to do. Returns the logical view name of an error page, passed
// to the view-resolver(s) in usual way.
// Note that the exception is NOT available to this view (it is not added
// to the model) but see "Extending ExceptionHandlerExceptionResolver"
// below.
return "databaseError";
}
// Total control - setup a model and return the view name yourself. Or
// consider subclassing ExceptionHandlerExceptionResolver (see below).
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
logger.error("Request: " + req.getRequestURL() + " raised " + ex);
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", req.getRequestURL());
mav.setViewName("error");
return mav;
}
}
Try the #ExceptionHandler annotation or #ControllerAdvice to create custom exception handling mechanisms:
https://www.tutorialspoint.com/spring_boot/spring_boot_exception_handling.htm
add #ResponseStatus(HttpStatus.BAD_REQUEST) on top of handleError(...) method.
#ExceptionHandler({ Throwable.class })
#ResponseStatus(HttpStatus.BAD_REQUEST)
public String handleError(...) {
...
}
Related
I'm new to Spring Boot. I'm trying to handle a exception when a value is null. Basically, its a value that I'm suppose to get from another external service (using restTemplate.exchange() method) which is currently down, so a null value gets assigned to that variable. Here's the code for the same service:
ResponseEntity<AuthenticationResponse> respNode = null;
try {
respNode = restTemplate.exchange(url, HttpMethod.POST, httpEntity, new ParameterizedTypeReference<AuthenticationResponse>() {});
} catch (Exception e) {
if (respNode == null) {
log.info("Unable to reach: {} ", url);
throw new AuthServerException("Auth Server Not Found");
}
}
I created a custom Exception to handle the scenario if the value is null. The Exception is thrown and I'm able to read it in the logs, but I'm not able to handle it.
AuthServerException.java
package com.nokia.sp.module.myVerify.portalapp.exception;
public class AuthServerException extends NullPointerException {
private static final long serialVersionUID = 1L;
public AuthServerException(String message) {
super(message);
}
}
Controller:
#Controller
#RequiredArgsConstructor
public class LandingPageController {
#ExceptionHandler(AuthServerException.class)
public ModelAndView AuthServerExceptionHandler(AuthServerException e) {
log.info(e.getMessage());
ModelAndView mav = new ModelAndView();
mav.setViewName("auth-server-handler");
return mav;
}
}
Am I missing something or have I made any syntax Error? Please do help me with the same. Thank you!
Right now i'm using this example of exception handling:
//get an object of type curse by id
//in the service file, this findCurseById() method throws a
//CursaNotFoundException
#GetMapping("/{id}")
public ResponseEntity<curse> getCursaById (#PathVariable("id") Long id) {
curse c = curseService.findCurseById(id);
return new ResponseEntity<>(c, HttpStatus.OK);
}
//so if not found, this will return the message of the error
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(CursaNotFoundException.class)
public String noCursaFound(CursaNotFoundException ex) {
return ex.getMessage();
}
and that's my exception
public class CursaNotFoundException extends RuntimeException {
public CursaNotFoundException(String s) {
super(s);
}
}
in future I want to use Angular as front-end, so I don't really know how I should treat the exceptions in the back-end. For this example let's say, should I redirect the page to a template.html page in the noCursaFound() method, or should I return something else? A json or something? I couldn't find anything helpful. Thanks
I would suggest keeping the error handling at the REST API level and not redirecting to another HTML page on the server side. Angular client application consumes the API response and redirects to template.html if needed.
Also, it would be better if the backend returns an ApiError when an exception occurs with a message and, optionally, an error code:
public class ApiError {
private String message;
private String code;
}
and handle the exceptions in a separate class, ExceptionHandler annotated with #ControllerAdvice:
#ControllerAdvice
public class ExceptionHandler {
#ExceptionHandler(value = CursaNotFoundException.class)
public ResponseEntity cursaNotFoundException(CursaNotFoundException cursaNotFoundException) {
ApiError error = new ApiError();
error.setMessase(cursaNotFoundException.getMessage());
error.setCode(cursaNotFoundException.getCode());
return new ResponseEntity(error, HttpStatus.NOT_FOUND);
}
#ExceptionHandler(value = Exception.class)
public ResponseEntity<> genericException(Exception exception) {
ApiError error = new ApiError();
error.setMessase(exception.getMessage());
error.setCode("GENERIC_ERROR");
return new ResponseEntity<>(error, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
My requirement is to access the custom exception thrown from first service along with it's body content in the second service
I have tried 2 things so far, FallbackFactory and ErrorDecoder, out of which only Fallback factory worked for me. Error decoder did not have the message of the exception which was thrown from other service. Here is the sample code that I found in another question:
There will be 2 services: inventory-service and product-service
inventory-service
InventoryController.java
#RestController
#RequestMapping("/inventories")
public class InventoryController {
private final ProductServiceClient productService;
public InventoryController(ProductServiceClient productService) {
super();
this.productService = productService;
}
#GetMapping
public ResponseEntity<?> companyInfo() {
return productService.hello();
}
}
ProductServiceClient.java
#FeignClient(name = "product-service", url = "http://localhost:9100", fallbackFactory = ProductServiceClientFallback.class)
public interface ProductServiceClient {
#GetMapping("/products")
ResponseEntity<?> hello();
}
#Component
class ProductServiceClientFallback implements FallbackFactory<ProductServiceClient> {
#Override
public ProductServiceClient create(Throwable cause) {
return new ProductServiceClient() {
#Override
public ResponseEntity<?> hello() {
System.out.println("hello!! fallback reason was " + cause.getMessage());
return ResponseEntity.ok().build();
}
};
}
}
product-service
ProductController.java
#RestController
#RequestMapping(value = "/products")
public class ProductController {
#GetMapping
public String hello() throws Exception {
if (true) {
throw new Exception("Service B Exception...");
}
return "Hello World";
}
}
ProductControllerAdvice.java
#RestControllerAdvice
public class ProductControllerAdvice {
#ExceptionHandler
public ResponseEntity<?> handleException(Exception exception) {
return new ResponseEntity<>("Caused due to : " + exception.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
}
}
So, when /inventories api is triggered in Inventory controller, it triggers a call to product-service via Feign Client and on product-service side, I throw a custom exception with a message, I have to access that message in my inventory-service.
To get that I have implemented fallback factory and it worked in a test-workspace since I got an output like this in console of inventory-service
hello!! fallback reason was status 500 reading ProductServiceClient#hello(); content:
Caused due to : Service B Exception...
But, my problem is when I try the similar approach with the applications that I'm working on, I did not get the message of exception, instead I got an out put like this:
reached fallback on workflow side, reason: status 400 reading ProvisioningServiceProxy#executeOrderAction(Long,Long,String)
Service-A
TestServiceA.java
#FeignClient( url = "/executeOrder", fallbackFactory = TestServiceAFallback.class )
public interface TestServiceA extends Serializable{
#PostMapping( value = "order/{requestId}/order/{orderId}/{command}" )
public ResponseEntity<ProcessInstanceVariable> executeOrderAction( #PathVariable( name = "command" ) String command );
}
Service-B from where the custom exception is thrown
TestServiceBController.java
#PostMapping( value = /executeOrder )
public ResponseEntity<ProcessInstanceVariable> executeOrderAction( #PathVariable( value = "command" ) String command )
{ //switch code to check the command value and throw exception for one particular command
throw new ValidationException("validation exception from service B");
}
I have an advice also, which handles Validation Exceptions and there is a method like this in that class
TestServiceBControllerAdvice.java
#ExceptionHandler( ValidationException.class )
public ResponseEntity<Object> handleValidationException( ValidationException ve )
{
return new ResponseEntity<>( ve.getMessage(), HttpStatus.BAD_REQUEST );
}
So, I was expecting to receive the message on TestServiceA side which I sent from TestServiceB, but I received a generic message showing that BAD REQUEST while reading the API.
I'm not sure if any extra configuration is required on TestServiceA side apart from below configuration:
testServiceA.properties
feign.hystrix.enabled=true
Let me know if anything is missing from my end, I have gone through this documentation and seems to me I have done the implementation the way it should happen to get the message and body of exception thrown from other service.
For anyone who comes to this question looking for some answers, I did end up implementing ErrorDecoder, which helped me in capturing the errors. The details are a little fade to me, how the message was caught.
But I used the below code:
public class CustomExceptionDecoder implements feign.codec.ErrorDecoder
{
#Override
public Exception decode( String methodKey,
Response response )
{
final ErrorDecoder defaultErrorDecoder = new Default();
try
{
if( response.body() != null )
{
byte[] bodyData = Util.toByteArray( response.body().asInputStream() );
String responseBody = new String( bodyData );
LOGGER.error( "Error captured in Custom Exception Decoder: ", responseBody );
return new CustomValidationException( responseBody );
}
}
catch( IOException e )
{
LOGGER.error( "Throwing IOException :: {}", e.getCause() );
}
return defaultErrorDecoder.decode( methodKey, response );
}
}
I am having different projects for Service and Web. I would like to know how to handle when specific exception comes from Services. For example I am handling DuplicateDataException as follows at Service side:
public void serviceFunction()
{
try
{
//code
}catch(DuplicateDataException e)
{
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(e.getMessage()).build();
}}
At UI side: controller class is calling the service function through Rest API
#RequestMapping(value = "/addNew", method = RequestMethod.POST)
public ModelAndView addNew(Object obj) {
try {
restTemplate.exchange(url, HttpMethod.POST, httpEntity,
Object.class);
LOGGER.info("Object Created Successfully");
} catch (Exception e) {
return ModelAndView("PageName", "param","value");
}
}
At UI side I am getting Internal Server Error, Instead I would like to get the entity error message value which was set at service side.
As a kind of best practice try to catch your exceptions in your service code and throw an RuntimeException("An error occured") or a self defined Exception which extends Java's RuntimeException. Then you can define a global ExceptionHandler for all of your controllers and return your own error page like:
#ControllerAdvice
public class MyExceptionHandler {
#ResponseStatus(HttpStatus.NOT_FOUND)
#ExceptionHandler(Exeption.class)
public ModelAndView handleFileNotFoundException(Exception exception){
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("yourView");
modelAndView.addObject("exception", exception);
return modelAndView;
}
}
In my Spring Boot Application, I'm currently leveraging the resources/public/error/404.html custom error page to show (automagically) this 404 page errors on invalid URLS.
Is there an easy way to retain this auto functionality, and add a simple log message (with the invalid URL) for every such 404 ?
Ideally with as little code as possible I would want some like :
//Some code
LOGGER.warn("Invalid URL " + request.url);
//Some more code
You need to define a custom ErrorViewResolver:
#Component
public class MyErrorViewResolver implements ErrorViewResolver {
#Override
public ModelAndView resolveErrorView(HttpServletRequest request, HttpStatus status, Map<String, Object> model) {
if (HttpStatus.NOT_FOUND == status) {
LoggerFactory.getLogger(this.getClass()).error("error 404 for url " + model.get("path"));
return new ModelAndView("error404", model);
}
else {
return new ModelAndView("error", model);
}
}
}
This MyErrorViewResolver will be automatically called in the BasicErrorController class.
For a 404 error, the view "error404" will be displayed.
For the other errors, the view "error" will be displayed.
Views must be in the "templates" folder (resources/templates/error404.html).
Add NotFoundException handler.
public class BaseController {
// Logger declaration
#ExceptionHandler(NotFoundException.class)
#ResponseStatus(value = HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorResponse handleNotFoundError(HttpServletRequest req, NotFoundException exception) {
List<Error> errors = Lists.newArrayList();
errors.add(new Error(String.valueOf(exception.getCode()), exception.getMessage()));
log.error(exception);
return new ErrorResponse(errors);
}
}