Throwing exceptions from Spring REST webservice as JSON/XML - java

I have a REST web service controller that looks like this:
#RequestMapping(value = URIConstants.URL_DOCUMENT_SEARCH, method = RequestMethod.POST, produces = { MediaType.APPLICATION_XML_VALUE, MediaType.APPLICATION_JSON_VALUE })
protected DocumentSearchResponse getDocuments(#Valid #ModelAttribute DocumentSearchRequest objDMSRequest,BindingResult bindingResult, HttpServletRequest objServletRequest) throws AppException
{
if (bindingResult.hasErrors())
{
//I want to throw my custom exception here
///Or can anyone suggest a more clean and efficient way
}
-----More code and logic
}
I have a custom exception and handlers that will throw invalid HTTP invalid request exception. The custom exception has errorcode and error description fields.
My requirement is is there a way to parse the error from the bindingresults to a custome exception and trow that in the controler.

What you can do:
return new ResponseEntity<String>(errorDescription,HttpStatus.BAD_REQUEST);
Or, you can do it hardcore if you really want to use Exception(not recommended):
try {
throw new CustomException();
} catch(CustomException e) {
e.printStackTrace();
return new ResponseEntity<String>(e.getErrorDescription(),e.getStatusCode());
}
By the way: returning a Exception it's not good, that's why I don't show it.

#Albert-Pinto first of all the way you are trying to do this is completely wrong. If you want to consume a object it should come as #RequestBody and not as just a simple object. What you have done in your example is a MVC way which we do with the web applications, not the way we do for web service. so the above code will become as
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method=RequestMethod.POST)
public ResponseEntity create(#RequestBody User user) {
try {
throw new CustomException();
} catch(CustomException e) {
e.printStackTrace();
return new ResponseEntity<String>(e.getErrorDescription(),e.getStatusCode());
}
}

Its is as simple as that
1. Create a class which extends Exception.
class MyCustomException extends Exception{
MyCustomException(){
}
MyCustomException(Object e){
super(e)
}
}
2. Make your code throw same type of exception
#Controller
#RequestMapping("/user")
public class UserController {
#RequestMapping(method=RequestMethod.POST)
public ResponseEntity create(#Valid User user, BindingResult bindingResult) {
try{
if (bindingResult.hasErrors()) {
throw new MyCustomException();
}
} catch(MyCustomException e){
//do what ever you want to do with it
}
...
}
3. Man you already done... :)

Related

how to get method signature from controlleradvice

I have some code like this
#Controller
class SomeController
#GetMapping("/someAsync")
#ResponseBody
public String someAsync() {
throw new SomeException();
}
#GetMapping("/somePage")
public String somePage() {
throw new SomeException();
}
in this case, i want to redirect to default error page for "/somePage"
and I need to response like { HttpStatus:500, message: "something happened" }
I made this ControllerAdvice
#ControllerAdvice
class SomeAdvice
#ExceptionHandler(Exception.class)
#ResponseBody
public Object handle(Exception e) {
// in this method, I want to know this Exception was called by a method that contains "#ResponseBody"
}
Could I know the method signature like this way?

Correct Pattern for handling Service Layer results

So I've been using Spring and Java for a while to build microservices. I am concerned by the way I am currently handling service layer results which uses "business exception"
Controller
#RestController
public class PurchaseController {
#Autowired
private PurchaseService purchaseService;
#PostMapping("/checkout")
public ResponseEntity<?> checkout(#RequestBody CheckoutRequest body) {
try {
SomeDTO dto = purchaseService.doCheckout(body);
return ResponseEntity.ok(dto);
}
catch (UnauthorizedException e) {
return ResponseEntity.status(HttpStatus.UNAUTHORIZED).body(e.getMessage());
}
catch (CustomBusinessException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
}
Service
#Service
public class PurchaseService {
// ...
public DTO doCheckout(CheckoutRequest request) {
// this one calls another microservice
if (!isUserValid(request.userId)) {
// current handling of business rules violation (1)
throw new UnauthorizedException("User not valid");
}
if (request.total < 10) {
// current handling of business rules violation (2)
throw new CustomBusinessException("Minimum checkout at 20 dollars");
}
// ... do actual checkout
return new DTO(someDTOData);
}
}
I was comfortable at using this "pattern" because I do not need to "if" the business result in the controller level to return the appropriate HttpStatusCode, but since I've found some articles saying that exception is expensive specifically in Java, I doubt what I was doing is good for the long run.
Is there another correct way to gracefully handles the business result layer?
The problem with ResponseEntity in Spring is that they are typed with the result object you want to return when the endpoint is called successfully, so you can't return another body different from the happy path one, that in your case would be SameDTO. One way to address this issue is to use ? as the type of the response entity, as you have done but it is not the most recommended way.
So the best way to do this is precisely to use exceptions when there is a situation when you can't return the expected object and you have to return another object or status code, but instead of using a try-catch in the controller you should use an exception handler (Controller Advice) https://www.baeldung.com/exception-handling-for-rest-with-spring.
This controller advice would catch any exception thrown in your application and depending on the exception type it could return a different response class or status code without affecting the main controller. One example of how can be your controller advice would be:
#ControllerAdvice
public class ErrorHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(RuntimeException.class)
public ResponseEntity<String> handleInternal(final RuntimeException ex) {
return ResponseEntity
.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(ex.getMessage());
}
#ExceptionHandler(UnauthorizedException.class)
public ResponseEntity<ResponseDto> identityClientException(UnauthorizedException e) {
return ResponseEntity
.status(HttpStatus.UNAUTHORIZED)
.body(e.getMessage());
}
#ExceptionHandler(CustomBusinessException.class)
public ResponseEntity<ResponseDto> identityClientException(CustomBusinessException e) {
return ResponseEntity
.status(HttpStatus.BAD_REQUEST)
.body(e.getMessage());
}
And your controller woulb be much more clean without exception handling logic:
#RestController
public class PurchaseController {
#Autowired
private PurchaseService purchaseService;
#PostMapping("/checkout")
public ResponseEntity<SomeDTO> checkout(#RequestBody CheckoutRequest body){
SomeDTO dto = purchaseService.doCheckout(body);
return ResponseEntity.ok(dto);
}
}

Redirect to error controller in case of any type of exception?

I am trying to create a Spring Boot application. I have created a simple demo method to showcase my issue.
#RequestMapping(value = "/customers", method = RequestMethod.GET)
public List<Customer> getCustomers() {
int i=0;
if(i==0){
throw new RuntimeException("test exception");
}
return (List<Customer>) jdbcCustomerDAO.findAll();
}
Here i am throwing a RuntimeException. I want to redirect it to ErrorController from where I want it to open error.jsp inside /src/main/webapp.
#Controller
public class MyCustomErrorController implements ErrorController{
#Override
public String getErrorPath() {
return "/error";
}
#GetMapping("/error")
public ModelAndView handleError(Model model) {
model.addAttribute("message", "Oops Sorry !!!!");
return new ModelAndView("error");
}
}
Error.jsp file
${message}
I am getting this error on page:
This page isn’t working
localhost is currently unable to handle this request.
HTTP ERROR 500
How can I redirect it to my error pagE?
Why not capture the exceptions using Spring own feature regarding errors? You can use something called ControllerAdvice (documented here). So, in your case, you can do something similar to the following:
#ControllerAdvice
public class MyCustomErrorController {
#ExceptionHandler(RuntimeException.class)
public ModelAndView handleError(RuntimeException exception) {
model.addAttribute("message", "Oops Sorry !!!!");
return new ModelAndView("error");
}
}
It might be that you can use this type of error handling instead. Let me know if something does not work as intended. A small reference can also be found on Spring' own blog.

How to have two ControllerAdvice in the same SpringMvc application

I would like to manage Exception thrown by simple Controller or RestController in two ways:
1) html redirection
2) Json error
I tested the code below :
#ControllerAdvice(annotations = Controller.class)
public class ExceptionHandlerController
{
#ExceptionHandler(Exception.class)
public ModelAndView handleException(HttpServletRequest _req, Exception _ex)
{
K_LOGGER.info("test");
return new ModelAndView();
}
}
#ControllerAdvice(annotations = RestController.class)
public class ExceptionHandlerRestController
{
#ExceptionHandler(Exception.class)
public ResponseEntity<String> handleException(HttpServletRequest _req, Exception _ex)
{
return new ResponseEntity<>("test", HttpStatus.INTERNAL_SERVER_ERROR);
}
}
#RestController
public class GreetingController
{
#RequestMapping("/greetingexception")
public Greeting greetingException(#RequestParam(value = "name", defaultValue = "World") String name)
throws Exception
{
throw new Exception();
}
}
It doesn't work properly, I always pass by ExceptionHandlerController but not by ExceptionHandlerRestController.
I think it's because #RestController inherit of #Controller.
Do you have a other solution?
Try to add #Order(Ordered.HIGHEST_PRECEDENCE) annotation to rest exception handler. It may helps you.
eg04lt3r answer is correct, just though that more details might be useful for someone.
In case when you have global #ControllerAdvice and want to handle some exception in a different way in one of your Controllers you need to set #Order(Ordered.HIGHEST_PRECEDENCE) on the #ControllerAdvice which should have higher priority.
For example:
#ControllerAdvice
public class GeneralExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#ControllerAdvice(assignableTypes = MyController.class)
#Order(Ordered.HIGHEST_PRECEDENCE)
public class MyExceptionHandler {
#ExceptionHandler(Exception.class)
protected ResponseEntity<Error> handleException(Exception ex) {
...
}
}
#Order is needed because on startup one of the handlers will register with higher order automatically, anyway and your exception handling will become unpredictable. For example I recently saw a case when if you start an app using bootRun gradle task MyExceptionHandler was primary, but when started as jar GeneralExceptionHandler was primary.

where to throw the HTTP Runtime Exception

Let's say I have the following runtime exception:
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public class ExpectationsFailedException extends RuntimeException {
public ExpectationsFailedException(String message) {
super(message);
}
}
My question is if it is ok to throw the previous HTTP exception in my service layer or should I throw it from my controller:
#Service
public class UserService {
#Autowired
...
public void addUser(final String email, final String username, final String password){
if(parameters_are_not_valid){
throw new ExpectationsFailedException("Invalid input");
}
}
}
The controller exception throwing solution would be the following:
#Service
public class UserService {
#Autowired
...
public void addUser(final String email, final String username, final String password) throws InvalidInputParameters {
if(parameters_are_not_valid){
throw new InvalidInputParameters("Invalid input");
}
}
}
and in my controller
#RestController
public class XController{
#Autowired
private UserService userService;
#RequestMapping(value = "/addUser", method = RequestMethod.POST)
public void addUser(#Valid #RequestBody SignUpForm form, BindingResult bindingResult){
if(bindingResult.hasErrors()){
throw new ExpectationsFailedException("Input parameters conditions were not fulfilled");
}
try {
userService.addUser(...);
}
catch(InvalidInputParameters ex){
throw new ExpectationsFailedException("Invalid service input parameters");
}
}
}
Which of those solutions is preferred? Why? I have a feeling that I should not throw HTTP exceptions in my services because I may use that services in other contexts which may not be related to HTTP.
I would go with the second one.
What do you think?
I agree with your last statement. Your service layer should be independent of HTTP or frontent frameworks (#ResponseStatus is Spring MVC annotation and therefore it's not the best practice to use it in your service layer).
However you don't have to throw one exception in service layer, catch it in controller and rethrow another exception annotated with #ResponseStatus. Just add exception handler for the service exception and return appropriate response status from it. You have plenty of options, for instance #ExceptionHandler:
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
#ExceptionHandler(InvalidInputParameters.class)
public void handle() {
// Do nothing, just return the status
}
You can put this code to #ControllerAdvice annotated class to enable it for all controllers or just in you controller if it's not needed elsewhere.

Categories