web-dev-qa-db-ja.com

春に例外の詳細を含むResponseEntityを返すことができません

Spring Restful ServiceとSpring MVCアプリケーションを作成しました。

Restful Service :: Restful Serviceは、DBに存在するエンティティを返します。存在しない場合は、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;
}

しかし、RestTemplateを使用してSpring MVCアプリケーションから上記のサービスを呼び出すと、有効なオブジェクトが存在する場合はそれが返されます。

要求されたオブジェクトが存在しない場合、Restfulサービスは例外情報を返していますが、呼び出し元(スプリングMVC)アプリケーションに到達していません。

Spring MVCアプリケーションはRestテンプレートを使用してRestful Webサービスを呼び出します

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

これは予想される動作です。 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();
}
4
user2683814

Exceptionhandlerで@ResponseBodyアノテーションを使用してみてください。例えば:

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

アプリケーションを開始しましたが、問題なく動作します。

メイヴン:

    <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

もう1つの違いは、コントローラーメソッドに@ResponseBodyがないことです。

0
Adina Fometescu

ケースを修正するには、カスタム例外ハンドラーを使用する必要があります。こんな感じ

@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