Прежде всего, вы можете написать оболочку для всего API. Аннотируйте его с помощью @Component, и вы можете использовать его где угодно, хотя Springs DI. Взгляните на этот пример проекта, который показывает сгенерированный код для клиента resttemplate с использованием генерации кода swagger.
Как вы сказали, вы безуспешно пытались реализовать настраиваемый обработчик ошибок респондента, я предполагаю, что API возвращает тело ответа "no record found"
, а код состояния - 200
.
Поэтому вы можете создать собственный AbstractHttpMessageConverter
, как упоминалось в моем втором ответе. Поскольку вы используете springs resttemplate, который использует objectmapper с jackson, нам не обязательно использовать этот очень общий суперкласс для создания нашего собственного. Мы можем использовать и расширить более подходящий AbstractJackson2HttpMessageConverter
класс. Реализация для вашего конкретного варианта использования может выглядеть следующим образом:
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.http.HttpInputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.http.converter.json.AbstractJackson2HttpMessageConverter;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Type;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;
public class WeirdAPIJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
public static final String NO_RECORD_FOUND = "no record found";
public WeirdAPIJackson2HttpMessageConverter() {
// Create another constructor if you want to pass an already existing ObjectMapper
// Currently this HttpMessageConverter is applied for every MediaType, this is application-dependent
super(new ObjectMapper(), MediaType.ALL);
}
@Override
public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException {
try (BufferedReader br = new BufferedReader(new InputStreamReader(inputMessage.getBody(), DEFAULT_CHARSET))) {
String responseBodyStr = br.lines().collect(Collectors.joining(System.lineSeparator()));
if (NO_RECORD_FOUND.equals(responseBodyStr)) {
JavaType javaType = super.getJavaType(type, contextClass);
if(Collection.class.isAssignableFrom(javaType.getRawClass())){
return Collections.emptyList();
} else if( Map.class.isAssignableFrom(javaType.getRawClass())){
return Collections.emptyMap();
}
return null;
}
}
return super.read(type, contextClass, inputMessage);
}
}
Пользовательский HttpMessageConverter проверяет тело ответа на наличие вашего конкретного сообщения «запись не найдена». В этом случае мы пытаемся вернуть значение по умолчанию в зависимости от общего типа возвращаемого значения. Atm возвращает пустой список, если тип возвращаемого значения является подтипом Collection, пустым набором для Set и null для всех других типов Class.
Кроме того, я создал RestClientTest с помощью MockRestServiceServer, чтобы продемонстрировать вам, как вы можете использовать свой RestTemplate в вышеупомянутом компоненте-оболочке API и как настроить его для использования нашего настраиваемого AbstractJackson2HttpMessageConverter.
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.client.RestClientTest;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.client.ExpectedCount;
import org.springframework.test.web.client.MockRestServiceServer;
import org.springframework.web.client.RestTemplate;
import java.util.List;
import java.util.Optional;
import static org.junit.Assert.*;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.method;
import static org.springframework.test.web.client.match.MockRestRequestMatchers.requestTo;
import static org.springframework.test.web.client.response.MockRestResponseCreators.withStatus;
@RunWith(SpringRunner.class)
@ContextConfiguration(classes = {RestTemplateResponseErrorHandlerIntegrationTest.MyObject.class})
@RestClientTest
public class RestTemplateResponseErrorHandlerIntegrationTest {
static class MyObject {
// This just refers to your MyObject class which you mentioned in your answer
}
private final static String REQUEST_API_URL = "/api/myobjects/";
private final static String REQUEST_API_URL_SINGLE = "/api/myobjects/1";
@Autowired
private MockRestServiceServer server;
@Autowired
private RestTemplateBuilder builder;
@Test
public void test_custom_converter_on_weird_api_response_list() {
assertNotNull(this.builder);
assertNotNull(this.server);
RestTemplate restTemplate = this.builder
.messageConverters(new WeirdAPIJackson2HttpMessageConverter())
.build();
this.server.expect(ExpectedCount.once(), requestTo(REQUEST_API_URL))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK).body(WeirdAPIJackson2HttpMessageConverter.NO_RECORD_FOUND));
this.server.expect(ExpectedCount.once(), requestTo(REQUEST_API_URL_SINGLE))
.andExpect(method(HttpMethod.GET))
.andRespond(withStatus(HttpStatus.OK).body(WeirdAPIJackson2HttpMessageConverter.NO_RECORD_FOUND));
ResponseEntity<List<MyObject>> response = restTemplate.exchange(REQUEST_API_URL,
HttpMethod.GET,
null,
new ParameterizedTypeReference<List<MyObject>>() {
});
assertNotNull(response.getBody());
assertTrue(response.getBody().isEmpty());
Optional<MyObject> myObject = Optional.ofNullable(restTemplate.getForObject(REQUEST_API_URL_SINGLE, MyObject.class));
assertFalse(myObject.isPresent());
this.server.verify();
}
}
person
FlorianDe
schedule
14.03.2019