Angularjs - Spring MVC Rest : how to handle exceptions - java

I am developping an single page app with angularjs and Spring Mcv Rest.
I am calling my service (mail sending with javax mail) like that in Angularjs : SendProformaFax.get({idCommande:$scope.commande.id})
And on server side my service :
#RequestMapping(value = "/sendProformaFax/{idCommande}",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#Timed
public void imprimeProforma(#PathVariable String idCommande) {
Commande commande = commandeRepository.findOne(new Long(idCommande));
List<Vente> ventes = venteRepository.findAllByCommande(commande);
blService.sendProformaFax(ventes);
}
I would like to display a message when the function sendProformaFax throws a MessagingException.
I don't know how to return this exception in my RestController and how to catch it in Angularjs.
If anyone can help me on this...
Thanks.
EDIT :
On server side I am doing this :
#ExceptionHandler(value = Exception.class)
public ErrorView defaultErrorHandler(HttpServletRequest req, Exception e) throws Exception {
// If the exception is annotated with #ResponseStatus rethrow it and let
// the framework handle it - like the OrderNotFoundException example
// at the start of this post.
// AnnotationUtils is a Spring Framework utility class.
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class) != null)
throw e;
// Otherwise setup and send the user to a default error-view.
ErrorView mav = new ErrorView();
mav.setException(e.getMessage());
mav.setUrl(req.getRequestURL().toString());
mav.setMessage("Veuillez contacter le support informatique.");
return mav;
}
On Angularjs side I am doing this
CreateFichierCiel.get({params:param}, function (response) {
$scope.infoMessage = "La génération du fichier CIEL est terminée."
$activityIndicator.stopAnimating();
$("#messageModal").modal('show');
$scope.find();
}, function (reason) {
$("#errorModal").modal('show');
})
But 'reason' object is like this :
config: Object data: Object error: "Internal Server Error" exception:
"java.lang.NullPointerException" message: "No message available" path:
"/api/createFichierCiel/15-00005" status: 500 timestamp: 1438430232307
proto: Object headers: function (name) { status: 500 statusText:
"Internal Server Error" proto: Object
So I am not getting the ErrorView class sent from the server.
If anyone can see where I am wrong here...
Thanks

You can make ExceptionHandler for MessagingException and set HTTPStatus to indicate that response has an error (egz. BAD_REQUEST)
#ExceptionHandler(MessagingException.class)
#ResponseStatus(HTTPStatus.BAD_REQUEST)
#ResponseBody
public ErrorView handleMessagingException(MessagingException ex) {
// do something with exception and return view
}
In AngularJS you can catch it from resource service like this:
MessagingService.get({idCommande: 1}, function (data) {
// this is success
}, function (reason) {
// this is failure, you can check if this is a BAD_REQUEST and parse response from exception handler
};
It almost the same when you use $http.

Adding to the answer by kTT, starting with Spring 4 you can wrap your #ExceptionHandler method in a class annotated with #ControllerAdvice so that you will have the same message for the same type of exception across the whole application. More you can look here

That is how I did it, we are using spring mvc and angularjs in our project.
I have this controllerAdvice class
#ControllerAdvice
public class ExceptionControllerAdvice {
#ExceptionHandler(ServiceException.class)
public ResponseEntity<ErrorResponse> rulesForCustomerNotFound(HttpServletRequest req, ServiceException e)
{
ErrorResponse error = new ErrorResponse();
error.portalErrorCode = e.getExceptionCode();
error.message = e.getMessage();
return new ResponseEntity<ErrorResponse>(error, HttpStatus.NOT_FOUND);
}
}
class ErrorResponse {
public int portalErrorCode;
public String message;
}
and then in restful controller where ServiceException is a customized runnable exception:
#Override
#RequestMapping("/getControls/{entity}")
public List<Control> getControls(#PathVariable(value="entity") String entity) throws ServiceException {
List<Control> controls = ImmutableList.of();
try {
controls = dao.selectControls(entity);
} catch (Exception e) {
logger.error("getting list of controls encountered an error ", e);
throw new ServiceException(50, "getting list of controls encountered an error.");
}
return controls;
}
in my app.js file in angularjs I use
.config(['$httpProvider', function ($httpProvider) {
$httpProvider.interceptors.push(function ($q, $location) {
return {
'response': function (response) {
//Will only be called for HTTP up to 300
return response;
},
'responseError': function (rejection) {
if(rejection.status === 0) {
alert('There is a problem connecting to the server. Is the server probably down?!');
}
else {
$location.url('/error').search({rejection: rejection});
}
return $q.reject(rejection);
}
};
});
}])
and in a error.controller.js
function init() {
ctrl.rejection = $location.search().rejection;
ctrl.portalErrorCode = ctrl.rejection.data.portalErrorCode;
ctrl.errorMessage = ctrl.rejection.data.message;
$log.info('An error occured while trying to make an ajax call' + ctrl.errorMessage + ': ' + ctrl.portalErrorCode);
}
and of course in error.tpl.html
<h2>
{{ctrl.rejection.status}} {{ctrl.rejection.statusText}}
</h2>
<h3 class="error-details">
Sorry, an error has occurred!
</h3>
<h3 class="error-details">
{{ctrl.errorMessage}}
</h3>

Related

How to return 404 not found in json format using restful api in java?

i would like to implement an exception handler for restful api if the uri is not matched.
For example: url is
localhost:8080\test\generateNumber will return
{"response_code":"200"}
andif the url is wrong for example:
localhost:8080\test\generateNumber2 will return
{"response_code":"404","message":"uri not found"}
i have no idea on how to do it. Can someone help?
I presume you're using Spring?
In that case you can use #ExceptionHandler like this:
#RestController
public class Example1Controller {
#GetMapping(value = "/testExceptionHandler", produces = APPLICATION_JSON_VALUE)
public Response testExceptionHandler(#RequestParam(required = false, defaultValue = "false") boolean exception)
throws BusinessException {
if (exception) {
throw new BusinessException("BusinessException in testExceptionHandler");
}
return new Response("OK");
}
#ExceptionHandler(BusinessException.class)
public Response handleException(BusinessException e) {
return new Response(e.getMessage());
}
}
And get a message in response.
More - in this manual.

Consuming custom error response from Spring REST

I want to implement custom error pages for error codes with messages.
Following baeldung guide so far i got this on the backend;
Custom Exception:
public class TicketNotFoundException extends RuntimeException
{
public TicketNotFoundException(Long id)
{
super("Ticket not found with id: "+id);
}
}
Custom Response:
public class CustomErrorResponse
{
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd hh:mm:ss")
private LocalDateTime timestamp;
private int status;
private String error;
//getters setters
}
Custom Exception Handler:
#ControllerAdvice
public class CustomGlobalExceptionHandler extends ResponseEntityExceptionHandler
{
#ExceptionHandler(value = TicketNotFoundException.class)
public ResponseEntity<CustomErrorResponse> customHandleNotFound(Exception ex)
{
CustomErrorResponse errors = new CustomErrorResponse();
errors.setTimestamp(LocalDateTime.now());
errors.setError(ex.getMessage());
errors.setStatus(HttpStatus.NOT_FOUND.value());
return new ResponseEntity<>(errors, HttpStatus.NOT_FOUND);
}
}
And Response itself works:
{ timestamp: "2020-04-13 09:33:52", status: 404, error: "Ticket not
found with id: 1" }
Backend terminal:
Resolved [com.eggorko.ebt.ticket.TicketNotFoundException: Ticket not found with id: 1]
So my question is what should I do on the client side?
Client controller looks like this:
#GetMapping("/{id}")
public String ticket(#PathVariable Long id, Model model)
{
String url = "http://localhost:8080/api/ticket/";
ResponseEntity<Ticket> ticket = restTemplate.getForEntity( url+ id, Ticket.class);
model.addAttribute("ticket",ticket.getBody());
model.addAttribute("title","Tickets");
return "ticket";
}
I did this bit client side, but it doesn't work:
#Controller
public class MyErrorController implements ErrorController
{
#RequestMapping("/error")
public String handleError(HttpServletRequest request)
{
Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
if (status != null) {
Integer statusCode = Integer.valueOf(status.toString());
if(statusCode == HttpStatus.NOT_FOUND.value()) {
return "error-404";
}
else if(statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()) {
return "error-500";
}
}
return "error";
}
#Override
public String getErrorPath()
{
return "/error";
}
}
This is what i got in terminal on client:
2020-04-13 10:34:40.559 ERROR 12868 --- [nio-8081-exec-8] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.client.HttpClientErrorException$NotFound: 404 : [{"timestamp":"2020-04-13 10:34:40","status":404,"error":"Ticket not found with id: 1"}]] with root cause
And it's goes to 500 error page, instead of 404.
I sort of understand why it doesn't work. I don’t have anything handling error on the client side and it goes to error 500. But i don’t know what to do with it.
UPDATE
So i did this:
String url = "http://localhost:8080/api/ticket/";
try {
ResponseEntity<Ticket> ticket = restTemplate.getForEntity(url + id, Ticket.class);
model.addAttribute("ticket",ticket.getBody());
model.addAttribute("title","Tickets");
}catch (Exception e)
{
String msg = e.getMessage();
model.addAttribute("message",msg);
return "/error";
}
return "ticket";
And now at least i'm getting a error page with the actual message from backend. But this solution is working around MyErrorController. MyErrorController dosen't fire and basicly obsolete.
You are returning view name from your MyErrorController, do you have a error page errro.html or error.jsp (whatever suffix you are using for your view resolver) at the location you specified in view resolver? Morever you are using same error page for all error code you can create multiple error pages for different error codes say error-404.jsp and error-500.jsp. Another approach is to return the ModelAndView instead of view name, you can refer to link.
UPDATE
Ok now i get what are you trying to achieve, It is giving 500 because you are not handling exception in your client controller, when you are making rest template call you are saying i want a response of Ticket type but you are getting response of CustomErrorResponse, determine which exception it is throwing either by console log or catching the exception and then handle it and do your processing.
#GetMapping("/{id}")
public String ticket(#PathVariable Long id, Model model)
{
String url = "http://localhost:8080/api/ticket/";
try
{
ResponseEntity<Ticket> ticket = restTemplate.getForEntity( url+ id,
Ticket.class);
}
catch(Exception e)
{
// handel exception
}
model.addAttribute("ticket",ticket.getBody());
model.addAttribute("title","Tickets");
return "ticket";
}
OR
You can wrap your Ticket and CustomErrorResponse is a parent class and then can use instance of to determine which response you received
ResponseEntity<Parent> parent= restTemplate.getForEntity( url+ id,
Parent.class);
if(parent.getBody() instanceof Ticket)
{
//normal flow
}
else
{
//error
}
UPDATE2
ErrorController is obsolete because
String url = "http://localhost:8080/api/ticket/";
try {
ResponseEntity<Ticket> ticket = restTemplate.getForEntity(url + id, Ticket.class);
model.addAttribute("ticket",ticket.getBody());
model.addAttribute("title","Tickets");
}catch (Exception e)
{
String msg = e.getMessage();
model.addAttribute("message",msg);
return "/error";
}
return "ticket";
is returning a view error.jsp(or .html) with model, it is not redirecting to URL /error, you can do that by using redirect see, but I suggest not to, your ErrorController is mean to deal error occurred in your application not in some application your application in calling to, implement a proper response page for such condition.
Happy coding!

Return exception from JAX-RS Rest Service as JSON

Is it in some way possible that an exception thrown from a rest service is returned as JSON? I have a JAX-RS Rest Service where I would like to achieve this. When I throw it now, it's mapped to an HTML response, which is not what i want. From what I have understood an ExceptionMapper will also map it to HTML? Is there any other alternative or libraries that allows the exception to be returned in JSON format?
It will respond as JSON.
#Provider
#Singleton
public class ExceptionMapperProvider implements ExceptionMapper<Exception>
{
#Override
public Response toResponse(final Exception exception)
{
return Response.status(HttpStatusCodes.STATUS_CODE_SERVER_ERROR).entity(new BasicResponse(InternalStatus.UNHANDLED_EXCEPTION, exception.getMessage())).type(MediaType.APPLICATION_JSON).build();
}
}
#XmlRootElement
public class BasicResponse {
public String internalStatus;
public String message;
public BasicResponse() {}
public BasicResponse(String internalStatus, String message){
this.internalStatus = internalStatus;
this.message = message;
}
}
You can create custom exception,It takes JSON request and response
#POST
#Path("/betRequest")
#Consumes({ "application/json", "application/x-www-form-urlencoded" })
#Produces({ "application/json", "application/x-www-form-urlencoded" })
public Response getBetRequest(String betRequestParams, #Context HttpServletRequest request)
{
BetResponseDetails betResponseDetails = new BetResponseDetails();
try{
//you code here
}
catch (JSONException ex)
{
ex.printStackTrace();
betResponseDetails.setResponseCode("9002");//your custom error code
betResponseDetails.setResponseStatus("Bad Request");//custom status
betResponseDetails.setResponseMessage("The request body contained invalid JSON");//custom error massage
return Response.status(200).entity(betResponseDetails).build();
}
}
Create One POJO BetResponseDetails
public class BetResponseDetails {
private String ResponseStatus;
private String ResponseCode;
private String ResponseMessage;
// getter/setter
.......
}
get the response data in a structure with status and data, if the status is error, show the correct message.
you can try this way
{
"status": "error",
"data": {
"message": "information of error message"
}
}
catch your exceptions, then build a response object in a standardized format such as
error: {
code: 'XXX',
status: HTTPStatus,
message: 'my error message'
}
And send it as a response with an error status (from Response.Status, usually 4xx or 5xx)

Angular $http - spring - throws Exception but http status 200

I got a behaviour I don't understand on an application using Spring and angular. An exception is Thrown in the http request. I did the test below.
#RequestMapping(value = "/contract/upload/excel", method = RequestMethod.POST)
public String uploadContractExcel(HttpServletRequest request, ModelMap model) {
if(true) {
throw new RuntimeException("my code is broken");
}
...
In JavaScript in the $http function instead of going into the error block, it returns to the success block with Status code 200 - OK. So I cannot handle any exception.
$http({
method : 'POST',
url : resolveAjax,
data : formData
}).then(
function successCallback(response) {
var data = response.data;
if (data.upload_error === "true") {
$scope.busy = false;
$scope.upload_error_message = data.upload_error_message;
} else {
$scope.contractSummary = angular
.fromJson(data.reference_excel_resolved);
$scope.busy = false;
$scope.tabindex = $scope.tabindex * 1 + 1;
}
},
function errorCallback(response) {
$scope.upload_error_message = 'Oups something went wrong';
});
Has anybody got an idea about what happens ?
Thanks
If you want your client to receive a bad HTTP status like 400 etc, you should return such status in your controller. Throwing an exception will not suffice.
You have a couple of options; don't throw the exception or create a #ControllerAdvice which handles exceptions for you.
PSEUDOCODE:
#RequestMapping(value = "/url", method = POST)
public ResponseEntity postYourObject(#RequestBody YourObject object) {
if (true) {
return new ResponseEntity<>("Something happened", HttpStatus.BAD_REQUEST);
}
}
Or keep throwing your exception and create a controller advice like this.
#ControllerAdvice
public class GlobalControllerBehavior {
#ExceptionHandler
public ResponseEntity handleException(YourException e) {
return new ResponseEntity<>(e.getMessage(), HttpStatus.BAD_REQUEST);
}
}
Bottom line, if you don't return a HTTP status code like 4xx or 5xx your JavaScript error block will not be excecuted.
I know its been long, but even if there is an exception on server side, it is ultimately a form of response that the client receives, and hence the HTTP status 200.

Spring MVC : the best way to handle exception for Ajax Request and normal request?

I want to define a common exception manger in my project, so I use #ControllerAdvice to do, the snippet of code is below:
#ExceptionHandler(Exception.class)
public ModelAndView handleAllException(HttpServletRequest request, Exception ex) throws Exception
{
LOGGER.error(ex.getMessage());
ModelAndView mav = new ModelAndView();
mav.addObject("exception", ex);
mav.addObject("url", request.getRequestURL());
mav.setViewName(ViewConstants.INTERNAL_ERROR_VIEW);
return mav;
}
it will return a common error page. That's great for normal exception of request. But if this is a Ajax request, the result is so ugly. so I add the code to judge it. The added code is below:
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
// return HTTP Status code and response message
} else {
// return error page name
}
I don't think it is the best way, anybody have a good opinion?
I have all my controllers in different packages based on whether they serve AJAX requests or not. Then I can set #basePackages element on the ControllerAdvice annotations to handle the exception accordingly
Update:
See RequestMapping#params and RequestMapping#headers to separate controllers based on headers and/or params
I would suggest to set error response code on any request, think this is a good practice to notify client that something goes wrong not depending on type of request. And for ajax request you can return same page and identify problem by error code.
If you use jQuery for making requests, you could use the following:
jQuery.ajaxSetup({
headers: { 'ajax-request': true },
statusCode: {
400: function (xhr) {
...do something
},
500: function (xhr) {
...do something
}
...
}
});
...
public class Application extends SpringBootServletInitializer {
#Bean(name = "simpleMappingExceptionResolver")
public SimpleMappingExceptionResolver createSimpleMappingExceptionResolver() {
SimpleMappingExceptionResolver r = new SimpleMappingExceptionResolver();
r.setDefaultErrorView("forward:/errorController");
return r;
}
#Controller
public class ErrorController {
public static final Logger LOG = Logger.getLogger(ErrorController.class);
#RequestMapping(value = "/errorController")
public ModelAndView handleError(HttpServletRequest request,
#RequestAttribute("exception") Throwable th) {
ModelAndView mv = null;
if ("XMLHttpRequest".equals(request.getHeader("X-Requested-With"))) {
if (isBusinessException(th)) {
mv = new ModelAndView("appAjaxBadRequest");
mv.setStatus(BAD_REQUEST);
} else {
LOG.error("Internal server error while processing AJAX call.", th);
mv = new ModelAndView("appAjaxInternalServerError");
mv.setStatus(INTERNAL_SERVER_ERROR);
}
mv.addObject("message", getUserFriendlyErrorMessage(th).replaceAll("\r?\n", "<br/>"));
} else {
LOG.error("Cannot process http request.", th);
mv = new ModelAndView("appErrorPage");
mv.addObject("exeption", th);
}
return mv;
}
}

Categories