it-roy-ru.com

Не удалось вернуть ResponseEntity с деталями исключения весной

Я создал Spring Restful Service и Spring MVC приложение.

Restful Service :: Restful service возвращает сущность, если она существует в БД. Если он не существует, он возвращает пользовательскую информацию об исключении в объекте ResponseEntity.

Он работает, как и ожидалось, протестировано с помощью Postman.

@GetMapping(value = "/validate/{itemId}", produces = { MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE })
public ResponseEntity<MyItem> validateItem(@PathVariable Long itemId, @RequestHeader HttpHeaders httpHeaders) {

    MyItem myItem = myitemService.validateMyItem(itemId);
    ResponseEntity<MyItem> responseEntity = null;
    if (myItem == null) {
        throw new ItemNotFoundException("Item Not Found!!!!");
    }
    responseEntity = new ResponseEntity<MyItem>(myItem, headers, HttpStatus.OK);
    return responseEntity;
}

Если запрашиваемая сущность не существует, Restful Service возвращается ниже.

@ExceptionHandler(ItemNotFoundException.class)
public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
    System.out.println("In CREEH::ItemNFE");
    ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
    ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<ExceptionResponse>(exceptionResponse, HttpStatus.NOT_FOUND);
    return responseEntity;
}

Но когда я вызываю вышеуказанную службу из весеннего MVC-приложения, используя RestTemplate, он возвращает действительный объект, если он существует.

Если запрошенный объект не существует, служба Restful возвращает информацию об исключении, но не достигает вызывающего (пружинного MVC) приложения.

Приложение Spring MVC вызывает Restful Web Service с использованием шаблона Rest

String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
int restCallStateCode = responseEntity.getStatusCodeValue();
4
Abdul

Это ожидаемое поведение. Шаблон Rest выдает исключение, когда http-статусом является ошибка клиента или сервера, и возвращает ответ, если http-статус не является ошибкой.

Вы должны предоставить реализацию для использования вашего обработчика ошибок, сопоставить ответ с объектом ответа и выдать исключение.

Создайте новый класс исключения ошибок с полем ResponseEntity.

public class ResponseEntityErrorException extends RuntimeException {
  private ResponseEntity<ErrorResponse> errorResponse;
  public ResponseEntityErrorException(ResponseEntity<ErrorResponse> errorResponse) {
      this.errorResponse = errorResponse;
  }
  public ResponseEntity<ErrorResponse> getErrorResponse() {
      return errorResponse;
  }
}

Пользовательский обработчик ошибок, который отображает ответ об ошибке обратно в ResponseEntity.

public class ResponseEntityErrorHandler implements ResponseErrorHandler {

  private List<HttpMessageConverter<?>> messageConverters;

  @Override
  public boolean hasError(ClientHttpResponse response) throws IOException {
    return hasError(response.getStatusCode());
  }

  protected boolean hasError(HttpStatus statusCode) {
    return (statusCode.is4xxClientError() || statusCode.is5xxServerError());
  }

  @Override
  public void handleError(ClientHttpResponse response) throws IOException {
    HttpMessageConverterExtractor<ExceptionResponse> errorMessageExtractor =
      new HttpMessageConverterExtractor(ExceptionResponse.class, messageConverters);
    ExceptionResponse errorObject = errorMessageExtractor.extractData(response);
    throw new ResponseEntityErrorException(ResponseEntity.status(response.getRawStatusCode()).headers(response.getHeaders()).body(errorObject));
  }

  public void setMessageConverters(List<HttpMessageConverter<?>> messageConverters) {
    this.messageConverters = messageConverters;
  }
}

Конфигурация RestTemplate. Необходимо установить errorHandler для RestTemplate равным ResponseEntityErrorHandler. 

@Configuration
public class RestTemplateConfiguration {
  @Bean
  public RestTemplate restTemplate() {
      RestTemplate restTemplate = new RestTemplate();
      ResponseEntityErrorHandler errorHandler = new ResponseEntityErrorHandler();
      errorHandler.setMessageConverters(restTemplate.getMessageConverters());
      restTemplate.setErrorHandler(errorHandler); 
      return restTemplate;
   }
}

Метод вызова

@Autowired restTemplate

String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";
try {
    ResponseEntity<Object> responseEntity = restTemplate.exchange(url, HttpMethod.GET, httpEntity, Object.class, uriParms);
    int restCallStateCode = responseEntity.getStatusCodeValue();
} catch (ResponseEntityErrorException re) {
    ResponseEntity<ErrorResponse> errorResponse = re.getErrorResponse();
}
3
Veeram

Попробуйте использовать аннотацию @ResponseBody в вашем Exceptionhandler. например:

public @ResponseBody ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {... }
1
Otto Touzil

Я запустил ваше приложение и работает просто отлично.

Maven:

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>

Класс контроллера это:

@Controller
public class ValidationController {


    @GetMapping(value = "/validate/{itemId}")
    public @ResponseBody ResponseEntity<MyItem> validateItem(@PathVariable Long itemId) {
        if (itemId.equals(Long.valueOf(1))) {
            throw new ItemNotFoundException();
        }
        return new ResponseEntity<>(new MyItem(), HttpStatus.OK);
    }

    @ExceptionHandler(ItemNotFoundException.class)
    public ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) {
        System.out.println("In CREEH::ItemNFE");
        ExceptionResponse exceptionResponse = new ExceptionResponse("Item Not Found Ex!!!", new Date(), webRequest.getDescription(false));
        ResponseEntity<ExceptionResponse> responseEntity = new ResponseEntity<>(exceptionResponse, HttpStatus.NOT_FOUND);
        return responseEntity;
    }
}

и тест:

@RunWith(SpringRunner.class)
@WebMvcTest(value = ValidationController.class, secure = false)

public class TestValidationController {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testExpectNotFound() throws Exception {
        mockMvc.perform(get("/validate/1"))
                .andExpect(status().isNotFound());
    }

    @Test
    public void testExpectFound() throws Exception {
        mockMvc.perform(get("/validate/2"))
                .andExpect(status().isOk());
    }
}

Вы уверены, что URL, который вы пытаетесь использовать с RestTemplate, правильный?

 String url = "http://localhost:8080/ItemServices/items/validate/{itemId}";

Ваш метод get - @GetMapping (value = "/ validate/{itemId}" 

Если у вас нет отображения запросов на уровне контроллера, URL должен быть:

 http://localhost:8080/validate/1

Другое отличие заключается в отсутствии @ResponseBody в вашем методе контроллера.

0
Adina

Вы должны использовать Custom Exception Handler, чтобы исправить ваш случай. Это выглядит так

@ControllerAdvice
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {

    public CustomResponseEntityExceptionHandler() {
        super();
    }
    // 404
    @ExceptionHandler(value = { EntityNotFoundException.class, ResourceNotFoundException.class })
    protected ResponseEntity<Object> handleNotFound(final RuntimeException ex, final WebRequest request) {      
        BaseResponse responseError = new BaseResponse(HttpStatus.NOT_FOUND.value(),HttpStatus.NOT_FOUND.name(),
                Constants.HttpStatusMsg.ERROR_NOT_FOUND);       
        logger.error(ex.getMessage());
        return handleExceptionInternal(ex, responseError, new HttpHeaders(), HttpStatus.NOT_FOUND, request);
    }

}

И ваш код должен выдать какое-то исключение, например:

if (your_entity == null) {
    throw new EntityNotFoundException("said something");
}

Если вы снова столкнетесь с этим случаем, вы просто выбросите исключение, как описано выше. Ваш обработчик позаботится обо всем остальном.

Надеюсь, это поможет.

0
David Pham