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.
Related
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.
I am using Spring REST to handle my requests.
Here is my code sample:
RestTemplate restTemplate = new RestTemplate();
HttpEntity entity = new HttpEntity(headers);
ResponseEntity<String> responseEntity = null;
try {
responseEntity = restTemplate.exchange(apiAddress + "action?uniqueId=" + id, HttpMethod.GET, entity, String.class);
}catch (RestClientException ex) {
System.out.println(responseEntity.toString());
String errorMessage = ex.getMessage();
}
Everything is OK when I have got 200 status and JSON with returned values.
The problem is with, for example, 404 JSONs.
During the debugging I have figured out that when 404 occurs my responseEntity is still null so I am unable to get the error code. Moreover I am unable to get JSON reply from the server which I know that it being send.
I tried HTTP Requester which works fine with 200 responses - giving me requested data and with 404 responses - giving me a JSON with error description.
Best Regards,
Karol
I have managed to figure out what is going on.
I Hope that it might help someone else:
I have implemented ResponseErrorHandler :
public class VariousErrorsResponseHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse clientHttpResponse) throws IOException {
if(clientHttpResponse.getStatusCode() == HttpStatus.NOT_FOUND || clientHttpResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) return false;
else return true;
}
#Override
public void handleError(ClientHttpResponse clientHttpResponse) throws IOException {
System.err.println(clientHttpResponse.getStatusCode() + "\n" + clientHttpResponse.getStatusText());
}
}
Then set it as restTemplate's error handler:
restTemplate.setErrorHandler(new VariousErrorsResponseHandler());
And done :)
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>
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;
}
}
what I'm trying to do...
I want to send a response message back to the calling jQuery AJAX method, that I can then use to create a warning popup message.
I tried using the HttpServletResponse method:
sendError(int sc, java.lang.String msg)
the problem...
Unfortunately, this method wraps the "msg" string value within a larger HTML string - which is apparently meant to be used as content for a standalone error page.
--I was hoping to just obtain the message string itself (i.e., using a jquery object method - e.g., "jqXHR").
How should I best accomplish this? (I.e., get the message back to the success/failure method without the HTML "wrapper")
...the java server snippet...
-
-
-
private List<MyDTO> performSearch(HttpSession session, HttpServletResponse resp)
{
try
{
// (code to inspect searchString, etc.)
//...forcing error for this test...
throw new Exception("bad request yaddah...yaddah...yaddah...");
}
catch (Exception e)
{
try
{
resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
}
catch (Exception e)
{
e.printStackTrace();
}
}
return null;
}
-
-
-
...the JQuery client snippet...
-
-
-
$.ajax({
type: "POST",
url: searchurl,
data: "searchString=" + searchString,
async: true,
success: function(jqXHR) {
alert("success");
},
error: function(jqXHR) {
alert("Error - bad search string used:" + jqXHR.responseText);
}
});
-
-
-
NOTE: Doing something like below, seems to work okay, but, I want to know if there is a better way to send a simple message string back to the ajax call ...
-
-
-
private List<MyDTO> performSearch(HttpSession session, HttpServletResponse resp)
{
try
{
// (inspect searchString, etc.)
//...forcing error for this test...
throw new Exception("bad request yaddah...yaddah...yaddah...");
}
catch (Exception e)
{
try
{
resp.setStatus(HttpServletResponse.SC_NOT_ACCEPTABLE);
resp.getWriter().write(e.getMessage());
resp.flushBuffer();
}
catch (Exception ex)
{
e.printStackTrace();
}
}
return null;
}
-
-
-
What is the simplest/best way to accomplish what I am trying to do?
Thanks for any help!!!
I don't know if you still need an answer after 3 years, but I'm working on a project and need to do the same thing. I use ResponseEntity as a return type for the methods in my Controller. It has a HttpStatus code (which will fire the success/error function from jQuery/AJAX) and a body (without the wrapper thing). You can rewrite your controller method like this:
Controller
private ResponseEntity performSearch(HttpSession session)
{
try
{
// (inspect searchString, etc.)
//...forcing error for this test...
throw new Exception("bad request yaddah...yaddah...yaddah...");
}
catch (Exception e)
{
return new ResponseEntity(e.getMessage, HttpStatus.BAD_REQUEST);
}
return null;
}
jQuery/AJAX
Your code is alright, just call jqXHR.responseText(). Also, the signature of the success function is (data, textStatus, jqXHR) (jqXHR comes third, not first).
ResponseEntity
ResponseEntity can be returned in multiple ways:
//Equivalent
return new ResponseEntity(someString, HttpStatus.OK);
return ResponseEntity.ok(someString);
return ResponseEntity.ok().body(someString);
ResponseEntity uses generics, like ResponseEntity<T>. So, if your method returns List you can use private ReponseEntity<List<MyDTO>>.
Hopefully this still helps you.
Your jQuery POST request is a success as long as you can return the value from the server side.
You don't get an error in your jquery (necessarily) even if you get it on the server.
You should distinguish between errors and successes in your server side response to the client. Jquery will execute the "success" callback either way!