春季无法返回带有异常详细信息的 ResponseEntity

Posted

技术标签:

【中文标题】春季无法返回带有异常详细信息的 ResponseEntity【英文标题】:Not able to return ResponseEntity with Exception Details in spring 【发布时间】:2019-02-10 12:08:12 【问题描述】:

我已经创建了一个 Spring Restful Service 和 Spring MVC 应用程序。

安心服务:: 如果实体存在于数据库中,则 Restful 服务返回一个实体。如果不存在则在 ResponseEntity 对象中返回一个自定义的 Exception 信息。

使用 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;

但是,当我使用 RestTemplate 从 Spring MVC 应用程序调用上述服务时,它会返回一个有效对象(如果存在)。

如果请求的对象不存在,Restful 服务正在返回异常信息,但它没有到达调用(spring MVC)应用程序。

Spring MVC 应用程序使用 Rest 模板调用 Restful Web Service

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();

【问题讨论】:

可能是 spring 无法在响应中将您的异常序列化为 JSON 正文。您可以删除produces = MediaType.APPLICATION_JSON_VALUE, MediaType.APPLICATION_XML_VALUE 并检查它是否会创建响应吗? 是同一个控制器中定义的异常处理程序,还是你有@ControllerAdvice类? @TA,我试过你的建议,没有用。 kukkuz,我有一个单独的@RestControllerAdvice 类 @Abdul 所以异常处理程序在@RestControllerAdvice 类中,而 validateItem@RestController 类中?只是检查 【参考方案1】:

这是预期的行为。当http状态为客户端错误或服务器错误时rest模板抛出异常,当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 配置 - 您必须将 RestTemplate 的 errorHandler 设置为 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();

【讨论】:

嗨 Veeram,ResponseEntityErrorException 应该在服务器端,而 ResponseEntityErrorHandler 应该在客户端实现对吧? 服务器端没有变化。一切都在客户端。注意在错误处理程序中使用异常。 我真的非常感谢你。我正在应用程序中实现这一点。你能告诉我怎么做吗 ** errorHandler.setMessageConverters(restTemplate.getMessageConverters()); ** 在 xml 中。 我创建了所有必需的代码设置,现在我可以看到你的类中的流程,但是因为我没有在 ErrorHandler 中设置消息转换器,所以它抛出了这个错误 - 请求处理失败;嵌套异常是 java.lang.IllegalArgumentException: 'messageConverters' 不能为空 不幸的是,xml 没有提供那种级别的控制。您必须手动将消息转换器添加到错误处理程序。【参考方案2】:

尝试在您的异常处理程序上使用@ResponseBody 注释。例如:

public @ResponseBody ResponseEntity<ExceptionResponse> itemNotFEx(WebRequest webRequest, Exception exception) ... 

【讨论】:

我试过了,但它不起作用,因为我已经在这样做了。我正在使用@RestControllerAdvice【参考方案3】:

您应该使用自定义异常处理程序来解决您的问题。看起来是这样的

@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");

如果您再次在其他地方遇到这种情况,您只需像上面那样抛出异常。你的处理程序会处理剩下的事情。

希望对您有所帮助。

【讨论】:

异常响应。它的 POJO。【参考方案4】:

我已经启动了您的应用程序并且工作正常。

马文:

    <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());
    

您确定您尝试与 RestTemplate 一起使用的 url 正确吗?

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

你的get方法是@GetMapping(value = "/validate/itemId"

如果您在控制器级别没有请求映射,则 url 应该是:

 http://localhost:8080/validate/1

另一个区别是控制器方法中缺少 @ResponseBody。

【讨论】:

以上是关于春季无法返回带有异常详细信息的 ResponseEntity的主要内容,如果未能解决你的问题,请参考以下文章

春季内部异常的自定义json响应

OAuth2用户详细信息未在春季安全(SpringBoot)中更新

有没有办法在没有投影的情况下在春季数据休息中返回带有 id 的关联对象

在itemwriter中,春季批处理未在运行时异常上滚动

ASP.Net Core 2 错误处理:如何在 Http Response 中返回格式化的异常详细信息?

在 Grails 服务中注入 LdapTemplate 以返回用户详细信息会导致空指针异常