I'm sending a response to another web service to create an user. If the user already exists it sends back the 409 response. I'm using RestTemplate like so:
#PostMapping("/todos/{toDoNoteId}/users")
public ResponseEntity <String> postUser(#RequestBody User user, #PathVariable int toDoNoteId, UriComponentsBuilder builder)throws HttpMessageNotReadableException, ParseException{
RestTemplate restTemplate = new RestTemplate();
final String uri = "http://friend:5000/users";
try {
ResponseEntity<String> result = restTemplate.postForEntity(uri, user, String.class);
return result;
}
catch (HttpClientErrorException ex) {
return ResponseEntity.status(ex.getRawStatusCode()).headers(ex.getResponseHeaders())
.body(ex.getResponseBodyAsString());
}
}
While catching an exception somewhat works (in the catch block i can access the status code and body), is there a way to access it without exceptions something similar like this:
#PostMapping("/todos/{toDoNoteId}/users")
public ResponseEntity <String> postUser(#RequestBody User user, #PathVariable int toDoNoteId, UriComponentsBuilder builder)throws HttpMessageNotReadableException, ParseException{
RestTemplate restTemplate = new RestTemplate();
final String uri = "http://friend:5000/users";
ResponseEntity<String> result = restTemplate.postForEntity(uri, user, String.class);
if(result.getStatusCode()=="409"){
// do something
}
else{
// do something else
}
return result;
}
Have you been check the ExceptionHandler? When exception throws, ExceptionHandler handles it.
For example:
#ControllerAdvice()
public class CustomExceptionHandler {
private static final Logger logger = LogManager.getLogger("CustomExceptionHandler");
#ExceptionHandler(YourException.class)
public ResponseEntity handleYourException(HttpServletRequest request, YourException ex) {
return ResponseEntity.ok("");
}
#ExceptionHandler(Exception.class)
public ResponseEntity handleException(HttpServletRequest request, Exception ex) {
logExp("Exception", request, ex);
//return new ResponseEntity<>();
return null;
}
}
You can create your own custom resttemplate and define exception handler. Here is a code snippet.
#Configuration
public class CustomRestTemplate extends RestTemplate {
#Autowired
private CustomErrorHandler customErrorHandler;
#PostConstruct
public void init() {
this.setErrorHandler(customErrorHandler);
}
}
#Component
public class CustomErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
if(response.getStatusCode() != "409"){
return true;
}else {
return false;
}
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
String responseBody = response.getBody();//Pls read from InputStream and create write into String
JSONObject jsonObj = new JSONObject(result);
JSONArray jsonArray = new JSONArray();
jsonObj.put("status", response.getStatusCode());
jsonObj.put("body", responseBody );
jsonArray.put(jsonObj);
responseString = jsonArray.get(0).toString();
throw new MyException(responseString );
}
}
class MyException throw RuntimeException {
public MyException (String message) {
super(message);
}
}
So, your class will changed to
#PostMapping("/todos/{toDoNoteId}/users")
public ResponseEntity <String> postUser(#RequestBody User user, #PathVariable int toDoNoteId, UriComponentsBuilder builder)throws HttpMessageNotReadableException, ParseException{
CustomRestTemplate restTemplate = new CustomRestTemplate ();
final String uri = "http://friend:5000/users";
ResponseEntity<String> result = restTemplate.postForEntity(uri, user, String.class);
return result
}
Related
I have an API which throws ResponseStatusException in case of error :
throw new ResponseStatusException(HttpStatus.NOT_FOUND, "My error msg");
In the client side I have :
RestTemplate restTemplate = this.builder.errorHandler(new RestTemplateResponseErrorHandler()).build();
// Then I use restTemplate to call URLs with restTemplate.exchange(...)
And the ResponseErrorHandler :
#Component
public class RestTemplateResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse httpResponse) throws IOException {
return (httpResponse.getStatusCode().series() == HttpStatus.Series.CLIENT_ERROR || httpResponse.getStatusCode()
.series() == HttpStatus.Series.SERVER_ERROR);
}
#Override
public void handleError(ClientHttpResponse httpResponse) throws IOException {
String error = new String(httpResponse.getBody().readAllBytes(), StandardCharsets.UTF_8);
}
}
Problem is the getBody() is empty, and when I inspect the httpResponse I cannot find the "My error msg", it seems that it's not present in the object.
Is there any way to get the msg?
Thx
You need to build the RestTemplate instance using the RestTemplateBuilder like this:
public class xyz {
RestTemplate restTemplate;
#Autowired
public xyz(RestTemplateBuilder restTemplateBuilder) {
restTemplate = restTemplateBuilder
.errorHandler(new RestTemplateResponseErrorHandler())
.build();
}
}
Catch HttpClientErrorException as follows:
try{
HttpEntity<ProfileDto> entity = new HttpEntity<ProfileDto>(profileDto,headers);
response= restTemplate.postForEntity(environment.getProperty("app.filedownload.url"),entity, xxxxxx.class);
}catch(HttpClientErrorException | HttpServerErrorException e ){
if(HttpStatus.UNAUTHORIZED.equals(e.getStatusCode())){
//do your stufs
}
}catch(Exception e){
e.printStackTrace();
}
everyone. I am now working in a Spring Boot project that one of the function is to send some HTTP requests to third party API and get the responses.
I decided to use RestTemplate as the http client and I created a bean for RestTemplate in a java file with #Configuration
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
restTemplate.setRequestFactory(new BufferingClientHttpRequestFactory(new SimpleClientHttpRequestFactory()));
List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
if (CollectionUtils.isEmpty(interceptors)) {
interceptors = new ArrayList<>();
}
interceptors.add(new RequestLoggingInterceptor());
restTemplate.setInterceptors(interceptors);
restTemplate.setErrorHandler(new RestResponseErrorHandler());
return restTemplate;
}
Custom error handler and logging interceptor are set in restTemplate. Then, I injected the RestTemplate into my service class by
#Autowired
RestTemplate restTemplate;
However, I found that my custom error handler is not working properly and I tried to check the error handler by
ResponseErrorHandler customErrorHandler = restTemplate.getErrorHandler();
I found that theErrorHandler is still DefaultResponseErrorHandler but not my custom errorHandler. Why? Is there anything wrong?
Updated
Below are my custom error handler and logging interceptor
public class RestResponseErrorHandler implements ResponseErrorHandler {
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) {
return true;
}
return false;
}
#Override
public void handleError(ClientHttpResponse response) throws IOException {
if (!response.getStatusCode().equals(HttpStatus.ACCEPTED)) {
throw new RestResponseErrorException(response.getStatusCode() + "" + response.getStatusText());
}
}
}
and
public class RequestLoggingInterceptor implements ClientHttpRequestInterceptor {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
logRequest(request, body);
ClientHttpResponse response = execution.execute(request, body);
logResponse(response);
return response;
}
private void logRequest(HttpRequest request, byte[] body) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Request begin -----------------------------");
log.debug("URI:{}", request.getURI());
log.debug("Method:{}", request.getMethod());
log.debug("Headers:{}", request.getHeaders().toString());
log.debug("Request body:{}", new String(body, "UTF-8"));
log.debug("Request end -----------------------------");
}
}
private void logResponse(ClientHttpResponse response) throws IOException {
if (log.isDebugEnabled()) {
log.debug("Response begin -----------------------------");
log.debug("Status code:{}", response.getStatusCode());
log.debug("Status text:{}", response.getStatusText());
log.debug("Headers:{}", response.getHeaders().toString());
log.debug("Response body:{}", StreamUtils.copyToString(response.getBody(), Charset.defaultCharset()));
log.debug("Response end -----------------------------");
}
}
}
I'm performing a Rest request in one of my controllers and I'd like to redirect to my error view if the request went wrong (404, 503 ...)
My controller calls this function :
public String functionTest(){
String date, res;
String url = "myRestUrl/{param}";
Map<String, String> uriParams = new HashMap<>();
uriParams.put("param", "param");
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(url);
HttpEntity<String> request = new HttpEntity<>(createHeaders());
RestTemplate restTemplate = new RestTemplate();
restTemplate.setErrorHandler(new RestErrorHandler());
ResponseEntity<String> response = restTemplate.exchange(builder.buildAndExpand(uriParams).toUri(), HttpMethod.GET, request, String.class);
res= response.getBody().getMyResult();
return res;
}
And here is my Rest error handler :
public class RestErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response) throws IOException {
//Here I need to use a ModelAndView to redirect to error view but I'm not anymore in my controller
}
}
I guess I'm doing it wrong, do you have any solutions ?
Here is a rather simplistic way to achieve what you need. If you would like more control/flexibility, refer to this Spring blog article.
In your (client) RestErrorHandler:
public class RestErrorHandler extends DefaultResponseErrorHandler {
#Override
public void handleError(ClientHttpResponse response)
throws IOException {
// Do some stuff here ...
// This could be your own exception, for example.
throw new IOException();
}
}
Then in your controller (or refer to the article above if you want other options):
// Your requestMappings here ...
#ExceptionHandler(Exception.class)
public ModelAndView handleError(HttpServletRequest req, Exception ex) {
ModelAndView mav = new ModelAndView();
// Do stuff and redirect to your error view.
return mav;
}
EDIT: Another solution would be to catch Spring's RestClientException, which is a RuntimeException thrown by RestTemplate when it encounters an error:
try {
restTemplate.exchange(...);
} catch (RestClientException ex) {
// Do stuff here ...
}
I have an endpoint who throw an Exception using spring annotation, here is the code of my Exception :
#ResponseStatus(value = HttpStatus.BAD_REQUEST)
public class MyException extends BaseApiException{
public MyException (String variable){
super("variable :"+variable+" can not be updated.");
}
}
When i use postman to test the Rest endpoint i get a correct result with a correct status code :
{
"errorType": "MyExption",
"message": "variable : XYZ can not be updated."
}
My problem is when i try to call the service using restTemplate I did not receive a body in the response, here is my code :
ResponseEntity<Document> response;
RestTemplate restTemplate = new RestTemplate();
response = restTemplate.exchange(builder.build().encode().toUri(), HttpMethod.POST, entity, Document.class);
you need to define an error handler to extract this
public class MyResponseErrorHandler implements ResponseErrorHandler {
private static final Logger log = LoggerFactory.getLogger(MyResponseErrorHandler.class);
#Override
public void handleError(ClientHttpResponse response) throws IOException {
//here you should be able to get //response.getBody()
log.error("Response error: {} {}", response.getStatusCode(), response.getStatusText());
}
#Override
public boolean hasError(ClientHttpResponse response) throws IOException {
return isError(response.getStatusCode());
}
public static boolean isError(HttpStatus status) {
HttpStatus.Series series = status.series();
return (HttpStatus.Series.CLIENT_ERROR.equals(series)
|| HttpStatus.Series.SERVER_ERROR.equals(series));
}
}
RestTemplate template = new RestTemplate();
template.setErrorHandler(new MyResponseErrorHandler());
Using Spring (4.2.4) with MVC (4.2.4) and Security (4.0.3). I have implemented an AccessDecisionManager and from within my decide-method I am throwing an exception:
public void decide(
Authentication authentication,
Object object,
Collection<ConfigAttribute> configAttributes
) throws AccessDeniedException, InsufficientAuthenticationException {
FilterInvocation fi = (FilterInvocation) object;
String requestUrl = fi.getRequestUrl();
...
throw new SessionCompanyNotRoleTableCompanyException(1, 2);
...
throw new AccessDeniedException("Access denied!");
}
I'm not able to catch neither "SessionCompanyNotRoleTableCompanyException" nor AccessDeniedException. I've tried using a global exception handler:
#Component
#ControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
#ExceptionHandler(SessionCompanyNotRoleTableCompanyException.class)
public ModelAndView sessionCompanyNotRoleTableCompany() {
log.debug("SessionCompanyNotRoleTableCompanyException captured in GlobalExceptionHandler");
String reason = "Reason: SessionCompanyNotRoleTableCompanyException";
ModelAndView mav = new ModelAndView();
mav.addObject("reason", reason);
mav.setViewName("error.html");
return mav;
}
#ExceptionHandler(Exception.class)
public ModelAndView exception(ModelMap model) {
log.debug("Exception captured in GlobalExceptionHandler");
String reason = "General Exception";
ModelAndView mav = new ModelAndView();
mav.addObject("reason", reason);
mav.setViewName("error.html");
return mav;
}
}
I've even created ExceptionResolver-classes like:
#Component
public class SessionCompanyNotRoleTableCompanyExceptionResolver implements HandlerExceptionResolver, Ordered {
private static final Logger log = LoggerFactory.getLogger(SessionCompanyNotRoleTableCompanyExceptionResolver.class);
private int order;
#Override
public ModelAndView resolveException(
HttpServletRequest request,
HttpServletResponse response,
Object handler,
Exception ex
) {
if (ex instanceof SessionCompanyNotRoleTableCompanyException) {
log.debug("SessionCompanyNotRoleTableCompanyException captured in SessionCompanyNotRoleTableCompanyExceptionResolver");
String reason = "Reason: SessionCompanyNotRoleTableCompanyException";
ModelAndView mav = new ModelAndView();
mav.addObject("reason", reason);
mav.setViewName("error.html");
return mav;
}
return null;
}
#Override
public int getOrder() {
return order;
}
public void setOrder(int order) {
this.order = order;
}
}
... and have them initialized in my web-config-class like:
#Bean
public SessionCompanyNotRoleTableCompanyExceptionResolver createSessionCompanyNotRoleTableCompanyExceptionResolver() {
SessionCompanyNotRoleTableCompanyExceptionResolver resolver = new SessionCompanyNotRoleTableCompanyExceptionResolver();
resolver.setOrder(1);
return resolver;
}
These work, i.e. exceptions are captured ONLY IF they are thrown from the Controllers. But NOT from my decide-method in the AccessDecisionManager.
What and how am I supposed to implement something that can catch these outside (before) the controller?
EDIT (adding the SessionCompanyNotRoleTableCompanyException to show you its definition):
public class SessionCompanyNotRoleTableCompanyException extends RuntimeException {
private static final long serialVersionUID = 1L;
public SessionCompanyNotRoleTableCompanyException(Long contextCompanyId, Long tableId) {
super("Context companyId: " + contextCompanyId + ", tableId: " + tableId);
}
}