Jersey/Jackson: how to catch json mapping exception? - java

I would like to catch json mapping exception in my restful service in case input json is not valid.
It throws org.codehaus.jackson.map.JsonMappingException, but I don't how to or where to catch this exception. I want to catch this exception and send back appropriate error response.
#JsonInclude(JsonInclude.Include.NON_NULL)
#Generated("org.jsonschema2pojo")
#JsonPropertyOrder({
"name",
"id"
})
public class Customer {
#JsonProperty("name")
private String name;
#JsonProperty("id")
private String id;
<setter/getter code>
}
public class MyService {
#POST
#Consumes(MediaType.APPLICATION_JSON)
public final Response createCustomer(#Context HttpHeaders headers,
Customer customer) {
System.out.println("Customer data: " + customer.toString());
return Response.ok("customer created").build();
}
}
Everything works fine, but if json body is not well formed then it throws JsonMappingException exception. I want to catch this exception.

What finally worked for me was to declare an ExceptionMapper provider for JsonMappingException, such as
import org.codehaus.jackson.map.JsonMappingException;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Component
#Provider
public class JsonMappingExceptionMapper implements ExceptionMapper<JsonMappingException> {
#Override
public Response toResponse(JsonMappingException exception) {
return Response.status(Response.Status.BAD_REQUEST).build();
}
}

Tested with Jersey 3, Jackson 2.x, and Grizzly HTTP server.
Create an ExceptionMapper as the #Provider to catch the JsonMappingException.
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import jakarta.ws.rs.core.Response;
import jakarta.ws.rs.ext.ExceptionMapper;
import jakarta.ws.rs.ext.Provider;
#Provider
public class CustomJsonExceptionMapper
implements ExceptionMapper<JsonMappingException> {
private static final ObjectMapper mapper = new ObjectMapper();
#Override
public Response toResponse(JsonMappingException exception) {
ObjectNode json = mapper.createObjectNode();
//json.put("error", exception.getMessage());
json.put("error", "json mapping error");
return Response.status(Response.Status.BAD_REQUEST)
.entity(json.toPrettyString())
.build();
}
}
Register the above exception mapper in ResourceConfig
public static HttpServer startHttpServer() {
final ResourceConfig config = new ResourceConfig();
config.register(YourResource.class);
// custom ExceptionMapper
config.register(CustomJsonExceptionMapper.class);
return GrizzlyHttpServerFactory.createHttpServer(BASE_URI, config);
}
Refer to this Jersey and Jackson example.

Related

How do I handle a Callable in my AOP Around advice?

I have a controller in my Spring Boot application. In my controller, I have an endpoint where I need to timeout the call if too much time elapses. I do this by returning a Callable from this method and including the config spring.mvc.async.request-timeout in my application.yml. This seems to work well for our purposes.
I also have an Aspect class in this application that contains a method that is triggered whenever a method in my controller is called. The point of this method is to log details such as the amount of time taken for an endpoint, what the response code was, and etc. This works well when the response of the method is not a Callable (ie. a ResponseEntity) since I can get response information from the return type without issue. However, I cannot get this response information when the method returns a Callable without invoking ((Callable) ProceedingJoinPoint.proceed()).call() from the aspect class. This makes API calls longer, and I believe that's because it invokes call() twice. Is there any way that I can get the response information without having to use call() in the Aspect class?
Here is a simple example of what I have so far in my aspect class:
#Around("...")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
Object result = joinPoint.proceed();
if (!(result instanceof Callable<?>)) {
// Do logging using result, which is a ResponseEntity...
} else {
Object callableResult = ((Callable<?>) result).call();
// Do logging using callableResult, which is a ResponseEntity...
}
return result;
}
Thank you.
I encountered the same situation at work a while ago and the following seems to solve it: Log response body after asynchronous Spring MVC controller method
Note: Try logging the async response in a class annotated with #ControllerAdvice and implements ResponseBodyAdvice instead. This should capture the real response instead of callable.
You could have a class annotated with #Aspects for logging request and #ControllerAdvice for logging response together.
e.g.
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.atomic.AtomicLong;
#Aspect
#ControllerAdvice
public class LoggingAdvice implements ResponseBodyAdvice {
private static final Logger LOGGER = LoggerFactory.getLogger(LoggingAdvice.class);
private static final AtomicLong ID = new AtomicLong();
#Before("within(com.example.demo.controller..*)")
public void endpointBefore(JoinPoint p) {
LOGGER.info(p.getTarget().getClass().getSimpleName() + " " + p.getSignature().getName() + " START");
Object[] signatureArgs = p.getArgs();
ObjectMapper mapper = new ObjectMapper();
mapper.enable(SerializationFeature.INDENT_OUTPUT);
try {
if (signatureArgs != null && signatureArgs.length > 0) {
LOGGER.info("\nRequest object: \n" + mapper.writeValueAsString(signatureArgs[0]));
} else {
LOGGER.info("request object is empty");
}
} catch (JsonProcessingException e) {
}
}
#Override
public boolean supports(MethodParameter returnType, Class converterType) {
return true;
}
#Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType, Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
long id = ID.incrementAndGet();
ServletServerHttpResponse responseToUse = (ServletServerHttpResponse) response;
HttpMessageConverter httpMessageConverter;
LoggingHttpOutboundMessageWrapper httpOutputMessage = new LoggingHttpOutboundMessageWrapper();
try {
httpMessageConverter = (HttpMessageConverter) selectedConverterType.newInstance();
httpMessageConverter.write(body, selectedContentType, httpOutputMessage);
LOGGER.info("response {}, {}, {}, {}, {}", id, responseToUse.getServletResponse().getStatus(), responseToUse.getServletResponse().getContentType(),
responseToUse.getHeaders(), httpOutputMessage.getResponseBodyInString());
} catch (InstantiationException | IllegalAccessException | IOException e) {
e.printStackTrace();
}
return body;
}
private static final class LoggingHttpOutboundMessageWrapper implements HttpOutputMessage {
private HttpHeaders httpHeaders = new HttpHeaders();
private ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
#Override
public OutputStream getBody() throws IOException {
return byteArrayOutputStream;
}
#Override
public HttpHeaders getHeaders() {
return httpHeaders;
}
public String getResponseBodyInString() {
return new String(byteArrayOutputStream.toByteArray());
}
}
}

Why is my custom exception not returning in JSON format?

I have connected a Spring applications to a React front-end where I need to display my custom exceptions. The custom exceptions work perfectly in Spring, but the front-end (React) only receives the error code (417) and nothing else.
I have determined that the problem is that the exception is not being returned in JSON format because the error message is displayed in its entirety when I use Postman, but not in JSON format.
My research has shown that since I am using a #RestController (for my main controller) and #ControllerAdvice (for my custom exception handler) that it should be returning in JSON format. I also tried adding a ResponseBody bean to the specific function but that did not help either.
Controller.java
package com.chess.controller;
import com.chess.board.*;
import com.chess.gameflow.Game;
import com.chess.gameflow.Move;
import com.chess.gameflow.Player;
import com.chess.gameflow.Status;
import com.chess.models.requests.BoardRequest;
import com.chess.models.requests.PlayerRequest;
import com.chess.models.requests.StatusRequest;
import com.chess.models.responses.MovesResponse;
import com.chess.models.responses.Response;
import com.chess.models.responses.StatusResponse;
import org.springframework.web.bind.annotation.*;
import java.util.List;
#CrossOrigin(origins= "http://localhost:3000", maxAge=7200)
#RestController
#RequestMapping("/game")
public class Controller {
Game game;
Board board = Board.boardConstructor();
#PostMapping("/players")
public List<Response> createPlayer(#RequestBody PlayerRequest request){
game = new Game(request.getName1(), request.getName2());
List<Response> returnValue = board.returnBoard();
Player player1= Game.players[0];
StatusResponse status = new StatusResponse(Status.isActive(), Status.isCheck(), player1);
returnValue.add(status);
return returnValue;
}
#PostMapping
public List<Response> makeMove(#RequestBody BoardRequest boardRequest){
StatusResponse status = Game.run(boardRequest);
List<Response> returnValue = board.returnBoard();
returnValue.add(status);
return returnValue;
}
#PostMapping("/end")
public StatusResponse endGame(#RequestBody StatusRequest statusRequest){
Status.setActive(false);
Board board = Board.boardConstructor();
board.generateBoard();
if (statusRequest.isForfeit()){
StatusResponse statusResponse = new StatusResponse(statusRequest.getPlayerName() + " declares defeat! Game Over!");
return statusResponse;
}
StatusResponse statusResponse = new StatusResponse("We have a draw! Good Game!");
return statusResponse;
}
#GetMapping("/moves")
public MovesResponse displayMoves(){
MovesResponse movesResponse = new MovesResponse(Move.returnMoveMessages());
return movesResponse;
}
}
CustomExceptionsHandler.java
package com.chess.exceptions;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class CustomExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = InvalidMoveException.class)
#ResponseBody //this line shouldn't be necessary as I am using a RestController but I added it anyways in one of my futile attempts and I don't think it should hurt
protected ResponseEntity<Object> resolveInvalidMove(InvalidMoveException e, WebRequest req) throws Exception {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
e.getMessage(),
req.getDescription(true));
return handleExceptionInternal(e, errorResponse.toString(), new HttpHeaders(), HttpStatus.EXPECTATION_FAILED, req);
#ExceptionHandler(value = MustDefeatCheckException.class)
protected ResponseEntity<Object> resolveCheckStatus(MustDefeatCheckException e, WebRequest req){
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
e.getMessage(),
req.getDescription(true));
return handleExceptionInternal(e, errorResponse, new HttpHeaders(), HttpStatus.EXPECTATION_FAILED, req);
}
}
ErrorResponse.java
package com.chess.exceptions;
import javax.xml.bind.annotation.XmlRootElement;
#XmlRootElement(name = "error")
public class ErrorResponse {
private int status;
private String errReason;
private String errMessage;
private String path;
public ErrorResponse(int status, String errReason, String errMessage, String path) {
this.status = status;
this.errReason = errReason;
this.errMessage = errMessage;
this.path = path;
}
#Override
public String toString(){
return "Status Code: " + status + " " + errReason + " Message: " + errMessage + " at " + path;
}
}
InvalidMoveException.java
package com.chess.exceptions;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ResponseStatus;
#ResponseStatus(value = HttpStatus.EXPECTATION_FAILED, reason="Invalid Move")
public class InvalidMoveException extends RuntimeException {
public InvalidMoveException(String msg){ super(msg); }
}
I solved half the problem. Instead of returning a ResponseEntity with handleExceptionInternal, I was able to return the ErrorResponse itself by making it a child of Response which is the father to all my regular responses.
So now my CustomExceptionsHandler.java looks like this-
#RestControllerAdvice
public class CustomExceptionsHandler extends ResponseEntityExceptionHandler {
#ExceptionHandler(value = InvalidMoveException.class)
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
public ErrorResponse resolveInvalidMove(InvalidMoveException e, WebRequest req) {
ErrorResponse errorResponse = new ErrorResponse(HttpStatus.EXPECTATION_FAILED.value(),
HttpStatus.EXPECTATION_FAILED.getReasonPhrase(),
e.getMessage(),
req.getDescription(true));
return errorResponse;
}
}
And I am getting the exception in JSON format when I use Postman. However, my error in
React is unchanged. I am still only getting the status code and no other information.
Board.js
DataService.makeMove(move)
.then(res => {
//console.log(res.data);
setIsWhite((prev) => !prev);
props.setTheBoard(res.data);
setStatus(res.data[64]);
updateMovesList();
})
.catch(err => {
console.log(err)
console.log(err.errMessage)
console.log(err.message)
console.log(err.status)
console.log(err.errReason)
})
DataService.js
makeMove(move){
return axios.post(`${url}`, move);
}
I always thought catching errors was very simple but apparently I am missing something

How can we postHandle an exception in HandlerInterceptorAdapter?

I am currently trying to implement a customized error handler for spring boot and I have done it with the following:
public class ExceptionHandler extends HandlerInterceptorAdapter {
public static Logger log = LoggerFactory.getLogger(LoggingInterceptor.class);
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, #Nullable ModelAndView modelAndView) throws Exception {
try {
log.info("Service {} Calling {} on {} finished with status {}",request.getRemoteUser(), request.getMethod(), request.getRequestURI(), HttpStatus.valueOf(response.getStatus()));
} catch (Exception e) {
// Do nothing
} finally {
log.error("[Spring Boot Interceptor] {} returned with {}", handler, HttpStatus.valueOf(response.getStatus()));
}
}
Somehow this does not work, and the exception is still thrown to the client, is there some way to catch the exception thrown by the method and ignore it for example.
A good way to manage the exception is using #ControllerAdvice, using this you may handle any kind of exception and customize the response as required.
As said in the comment, you have to add InterceptorRegistry to register the interceptor.
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
#Configuration
public class WebConfig implements WebMvcConfigurer {
#Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new Interceptor()).addPathPatterns("/**");
}
}
The catch block inside postHandle will only be executed if an exception occurred inside the try-catch block as below,
#Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, #Nullable ModelAndView modelAndView) throws Exception {
try {
int error = 1/0;
} catch (Exception e) {
log.info("Exception will be handled inside catch block");
}
}
Now let's explore the #ControllerAdvice to manage the exception within the application. These two APIs will generate the exceptions and we will manage the exceptions using #ExceptionHandler
#GetMapping("/exception/404")
public void generateResourceNotFound() {
throw new ResourceNotFoundException("resource not found");
}
#GetMapping("/exception/403")
public void generateAccessDenied() {
throw new AccessDeniedException("access denied");
}
GlobalExceptionHandler.java
import com.learning.annotations.controller.ResourceNotFoundException;
import com.learning.annotations.dto.ErrorResponseDTO;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.context.request.WebRequest;
import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
#ControllerAdvice
public class GlobalExceptionHandler extends ResponseEntityExceptionHandler {
public Logger log = LoggerFactory.getLogger(Interceptor.class);
#ExceptionHandler(AccessDeniedException.class)
public ResponseEntity<ErrorResponseDTO> handleAccessDeniedException(AccessDeniedException ex, WebRequest request) {
ErrorResponseDTO response = new ErrorResponseDTO();
response.setError(ex.getMessage());
response.setMessage("You don't have authority to access the resource");
return new ResponseEntity<>(response, HttpStatus.FORBIDDEN);
}
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<ErrorResponseDTO> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ErrorResponseDTO response = new ErrorResponseDTO();
response.setError(ex.getMessage());
response.setMessage("Resource might be moved temporary or not available");
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
}
To customize the response we can create error response DTO as follows,
import lombok.Data;
#Data
public class ErrorResponseDTO {
private String message;
private String error;
}

Add Query Parameter to Every REST Request using Spring RestTemplate

Is there a way to add a query parameter to every HTTP request performed by RestTemplate in Spring?
The Atlassian API uses the query parameter os_authType to dictate the authentication method so I'd like to append ?os_authtype=basic to every request without specifying it all over my code.
Code
#Service
public class MyService {
private RestTemplate restTemplate;
#Autowired
public MyService(RestTemplateBuilder restTemplateBuilder,
#Value("${api.username}") final String username, #Value("${api.password}") final String password, #Value("${api.url}") final String url ) {
restTemplate = restTemplateBuilder
.basicAuthorization(username, password)
.rootUri(url)
.build();
}
public ResponseEntity<String> getApplicationData() {
ResponseEntity<String> response
= restTemplate.getForEntity("/demo?os_authType=basic", String.class);
return response;
}
}
You can write custom RequestInterceptor that implements ClientHttpRequestInterceptor
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpRequest;
import org.springframework.http.client.ClientHttpRequestExecution;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.http.client.ClientHttpResponse;
public class AtlassianAuthInterceptor implements ClientHttpRequestInterceptor {
#Override
public ClientHttpResponse intercept(
HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
// logic to check if request has query parameter else add it
return execution.execute(request, body);
}
}
Now we need to configure our RestTemplate to use it
import java.util.Collections;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.web.client.RestTemplate;
#Configuration
public class MyAppConfig {
#Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate(clientHttpRequestFactory());
restTemplate.setInterceptors(Collections.singletonList(new AtlassianAuthInterceptor()));
return restTemplate;
}
}
For the ones interested in logic to add a query parameter, as HttpRequest is immutable a wrapper class is needed.
class RequestWrapper {
private final HttpRequest original;
private final URI newUriWithParam;
...
public HttpMethod getMethod() { return this.original.method }
public URI getURI() { return newUriWithParam }
}
Then in your ClientHttpRequestInterceptor you can do something like
public ClientHttpResponse intercept(
request: HttpRequest,
body: ByteArray,
execution: ClientHttpRequestExecution
) {
URI uri = UriComponentsBuilder.fromUri(request.uri).queryParam("new-param", "param value").build().toUri();
return execution.execute(RequestWrapper(request, uri), body);
}
Update
Since spring 3.1 wrapper class org.springframework.http.client.support.HttpRequestWrapper is available in spring-web

How can to send request from RestyGWT part to another server project?

I have restyGWT+GXT project, that send request to server project (Spring Boot), so, my restyGWT+GXT part:
buiid.gradle:
...
compile 'org.fusesource.restygwt:restygwt:2.0.3'
compile 'javax.ws.rs:jsr311-api:1.1.1'
my rest service in restyGWT+GXT part:
import org.fusesource.restygwt.client.MethodCallback;
import org.fusesource.restygwt.client.RestService;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;
import java.util.List;
public interface LoadHelloService extends RestService {
#GET
#Path("/rest/loadHelloService")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public void loadHelloService(MethodCallback<List<Hello>> callback);
}
bean Hello.java:
public class Hello {
private final String id;
private final String name;
#JsonCreator
public Hello(#JsonProperty("id") String id, #JsonProperty("name") String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public String getName() {
return name;
}
}
in MainMenuPage (implements IsWidget):
on click menuButton1 send request to server project (Spring Boot):
#UiHandler("menuButton1")
void selectOnMenu1(SelectEvent event) {
...
restServerLoader.loadHelloListFromServer();
}
so, RestServerLoader class with method loadHelloListFromServer:
import com.google.gwt.core.client.GWT;
import com.sencha.gxt.widget.core.client.box.MessageBox;
import org.fusesource.restygwt.client.*;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class RestServerLoader {
public void loadHelloListFromServer() {
String pageBaseUrl = "http://127.0.0.1:8080/";
Defaults.setServiceRoot(pageBaseUrl);
Map<String, String> headers = new HashMap<>();
headers.put("Access-Control-Allow-Methods", "GET");
Resource resource = new Resource(Defaults.getServiceRoot(), headers);
LoadHelloService service = GWT.create(LoadHelloService.class);
((RestServiceProxy)service).setResource(resource);
service.loadHelloService(new MethodCallback<List<Hello>>() {
public void onSuccess(Method method, List<Hello> response) {
MessageBox messageBox = new MessageBox("response (list) = " + response.toString());
messageBox.show();
//code your stuff here
}
public void onFailure(Method method, Throwable exception) {
MessageBox messageBox = new MessageBox("exception = " + exception);
messageBox.show();
//code your stuff here
}
});
}
}
So, and when I send request loadHelloService I have: org.fusesource.restygwt.client.FailedStatusCodeException: status code 0.
:(((((((
my server part (Spring Boot) rest:
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import java.util.ArrayList;
import java.util.List;
#Path("/")
#Consumes(MediaType.APPLICATION_JSON)
#Produces(MediaType.APPLICATION_JSON)
public class HelloResource {
#Context
private HttpServletResponse response;
#OPTIONS
#Path("loadHelloService") //The response for the preflight request made implicitly by the bowser
public Response loadHelloPreflight() {
Response response = Response.ok()
.header("Access-Control-Allow-Origin", "*")
.header("Access-Control-Allow-Methods", "POST, GET, UPDATE, OPTIONS")
.header("Access-Control-Allow-Headers", "*")
.header("Access-Control-Max-Age", "18000").build();
return response;
}
#GET
#Path("loadHelloService")
public List<Hello> loadHelloList() {
response.addHeader("Access-Control-Allow-Origin", "*");
response.addHeader("Access-Control-Allow-Methods", "POST, GET, UPDATE, OPTIONS");
response.addHeader("Access-Control-Allow-Headers", "*");
List<Hello> list = new ArrayList<>();
list.add(new Hello("1", "ronan"));
list.add(new Hello("2", "john"));
return list;
}
}
so, When I send request, I input method loadHelloPreflight, but when send request to loadHelloList I have: org.fusesource.restygwt.client.FailedStatusCodeException: status code 0., Why??? :((, When I send request to server part (Spring Boot) from browser Postman Client - all good!, I get list of hellos, but I want do it from restyGWT+GXT part :((( Help me, please.
Did you forget some cors headers like below ?
.header("Access-Control-Allow-Headers", "x-http-method-override");

Categories