Springboot — отправлять HTTP-запросы более элегантным способом (детали RestTemplate)

Java задняя часть HTTP Apache

Эта статья была авторизована для эксклюзивной публикации «Выбор серверной технологии».

RestTemplateдаSpringКлиент предоставил доступ к сервису Rest,RestTemplateПредоставляет множество удобных методов доступа к удаленным службам Http, которые могут значительно повысить эффективность записи клиента.

Моя предыдущая разработка HTTP была разработана с помощью HttpClient от apache, код был сложным, и мне приходилось беспокоиться о повторном использовании ресурсов. Код очень сложный и много лишнего. Сделайте скриншот. Это инструмент пост-запроса, который я инкапсулировал:

Это руководство поможет вам реализовать RestTemplate в экосистеме Spring.Get请求иPost请求иexchange指定请求类型практика иRestTemplateПрочитав анализ исходного кода основного метода, вы будете элегантно отправлять HTTP-запросы.

1. Кратко опишите RestTemplate

даSpringБазовый класс, используемый для синхронизации на стороне клиента, что упрощает иhttpобслуживать связь и встречатьсяRestFulВ принципе, программный код может дать ему URL и извлечь результат. по умолчанию,RestTemplateПо умолчанию он использует инструмент HTTP-соединения jdk. Конечно, вы также можетеsetRequestFactoryсвойство для переключения на другой источник HTTP, напримерApache HttpComponents,NettyиOkHttp.

RestTemplate может значительно упростить отправку данных формы и поставляется с функцией автоматического преобразования данных JSON, но только поняв состав HttpEntity (заголовок и тело) и поняв разницу с uriVariables, вы сможете по-настоящему понять его использование. Это более заметно в почтовых запросах, которые будут представлены ниже.

Запись этого класса в основном сформулирована в соответствии с шестью методами HTTP:
HTTP method RestTemplate methods
DELETE delete
GET getForObject
getForEntity
HEAD headForHeaders
OPTIONS optionsForAllow
POST postForLocation
postForObject
PUT put
any exchange
execute

Кроме того, обмен и выполнение могут использовать описанный выше метод в целом.

Внутренний,RestTemplateИспользовать по умолчаниюHttpMessageConverterэкземпляр будетHTTPсообщение вPOJOили изPOJOпреобразовать вHTTPИнформация. Преобразователи для основного MIME-типа зарегистрированы по умолчанию, но их также можноsetMessageConvertersЗарегистрируйте другие преобразователи. (На самом деле это не заметно при его использовании. Многие методы имеют параметр responseType, который позволяет вам передать объект, отображаемый телом ответа, а затем нижний слой использует HttpMessageConverter для его отображения)

HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);

HttpMessageConverter.javaИсходный код:

public interface HttpMessageConverter<T> {
        //指示此转换器是否可以读取给定的类。
	boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);

        //指示此转换器是否可以写给定的类。
	boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);

        //返回List<MediaType>
	List<MediaType> getSupportedMediaTypes();

        //读取一个inputMessage
	T read(Class<? extends T> clazz, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException;

        //往output message写一个Object
	void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage)
			throws IOException, HttpMessageNotWritableException;

}

Внутренний,RestTemplateИспользовать по умолчаниюSimpleClientHttpRequestFactoryиDefaultResponseErrorHandlerобрабатываться отдельноHTTPсоздания и ошибок, но также может бытьsetRequestFactoryиsetErrorHandlerпокрывать.

2. получить практику запроса

2.1. Метод getForObject()

public <T> T getForObject(String url, Class<T> responseType, Object... uriVariables){}
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> uriVariables)
public <T> T getForObject(URI url, Class<T> responseType)

getForObject()На самом деле, чемgetForEntity()Он содержит больше функций для преобразования HTTP в POJO, ноgetForObjectбез обработкиresponseСпособность. Потому что он принимает формуpojo. многое пропустилresponseИнформация.

2.1.1 POJO:
public class Notice {
    private int status;
    private Object msg;
    private List<DataBean> data;
}
public  class DataBean {
  private int noticeId;
  private String noticeTitle;
  private Object noticeImg;
  private long noticeCreateTime;
  private long noticeUpdateTime;
  private String noticeContent;
}
Пример: 2.1.2 получить запрос без параметров
	/**
	 * 不带参的get请求
	 */
	@Test
	public void restTemplateGetTest(){
		RestTemplate restTemplate = new RestTemplate();
		Notice notice = restTemplate.getForObject("http://xxx.top/notice/list/1/5"
				, Notice.class);
		System.out.println(notice);
	}

Отпечатки консоли:

INFO 19076 --- [           main] c.w.s.c.w.c.HelloControllerTest          
: Started HelloControllerTest in 5.532 seconds (JVM running for 7.233)

Notice{status=200, msg=null, data=[DataBean{noticeId=21, noticeTitle='aaa', noticeImg=null, 
noticeCreateTime=1525292723000, noticeUpdateTime=1525292723000, noticeContent='<p>aaa</p>'}, 
DataBean{noticeId=20, noticeTitle='ahaha', noticeImg=null, noticeCreateTime=1525291492000, 
noticeUpdateTime=1525291492000, noticeContent='<p>ah.......'
Пример: 2.1.3 получить запрос с параметрами 1
Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/{1}/{2}"
				, Notice.class,1,5);

Проницательный человек может с первого взгляда увидеть, что используется заполнитель{1}.

Пример: 2.1.4 получить запрос с параметрами 2
		Map<String,String> map = new HashMap();
		map.put("start","1");
		map.put("page","5");
		Notice notice = restTemplate.getForObject("http://fantj.top/notice/list/"
				, Notice.class,map);

На первый взгляд, проницательный человек использует карту для загрузки параметров, но она парсит по умолчаниюPathVariableформа URL.

2.2 Метод getForEntity()
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Object... uriVariables){}
public <T> ResponseEntity<T> getForEntity(String url, Class<T> responseType, Map<String, ?> uriVariables){}
public <T> ResponseEntity<T> getForEntity(URI url, Class<T> responseType){}

В отличие от метода getForObject(), возвращается следующее:ResponseEntityObject, если вам нужно преобразовать его в pojo, вам также необходимо ввести класс инструмента json, который используется в соответствии с личными предпочтениями. Может ли Baidu не анализировать jsonFastJsonилиJacksonи другие инструменты. Потом будем учитьсяResponseEntityКакой метод ниже.

ResponseEntity, HttpStatus, структура BodyBuilder

ResponseEntity.java

public HttpStatus getStatusCode(){}
public int getStatusCodeValue(){}
public boolean equals(@Nullable Object other) {}
public String toString() {}
public static BodyBuilder status(HttpStatus status) {}
public static BodyBuilder ok() {}
public static <T> ResponseEntity<T> ok(T body) {}
public static BodyBuilder created(URI location) {}
...

HttpStatus.java

public enum HttpStatus {
public boolean is1xxInformational() {}
public boolean is2xxSuccessful() {}
public boolean is3xxRedirection() {}
public boolean is4xxClientError() {}
public boolean is5xxServerError() {}
public boolean isError() {}
}

BodyBuilder.java

public interface BodyBuilder extends HeadersBuilder<BodyBuilder> {
    //设置正文的长度,以字节为单位,由Content-Length标头
      BodyBuilder contentLength(long contentLength);
    //设置body的MediaType 类型
      BodyBuilder contentType(MediaType contentType);
    //设置响应实体的主体并返回它。
      <T> ResponseEntity<T> body(@Nullable T body);
}

Видно, что ResponseEntity содержит информацию HttpStatus и BodyBuilder, что нам удобнее для работы с нативными вещами response.

Пример:

@Test
public void rtGetEntity(){
		RestTemplate restTemplate = new RestTemplate();
        ResponseEntity<Notice> entity = restTemplate.getForEntity("http://fantj.top/notice/list/1/5"
                , Notice.class);

        HttpStatus statusCode = entity.getStatusCode();
        System.out.println("statusCode.is2xxSuccessful()"+statusCode.is2xxSuccessful());

        Notice body = entity.getBody();
        System.out.println("entity.getBody()"+body);


        ResponseEntity.BodyBuilder status = ResponseEntity.status(statusCode);
        status.contentLength(100);
        status.body("我在这里添加一句话");
        ResponseEntity<Class<Notice>> body1 = status.body(Notice.class);
        Class<Notice> body2 = body1.getBody();
        System.out.println("body1.toString()"+body1.toString());
    }
statusCode.is2xxSuccessful()true
entity.getBody()Notice{status=200, msg=null, data=[DataBean{noticeId=21, noticeTitle='aaa', ...
body1.toString()<200 OK,class com.waylau.spring.cloud.weather.pojo.Notice,{Content-Length=[100]}>

Конечно, естьgetHeaders()Примеров других способов нет.

3. Практика отправки запросов

Точно так же почтовый запрос также имеетpostForObjectиpostForEntity.

public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Object... uriVariables)
			throws RestClientException {}
public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
			throws RestClientException {}
public <T> T postForObject(URI url, @Nullable Object request, Class<T> responseType) throws RestClientException {}

Пример

Я использую интерфейс для проверки почтового ящика для тестирования.

@Test
public void rtPostObject(){
    RestTemplate restTemplate = new RestTemplate();
    String url = "http://47.xxx.xxx.96/register/checkEmail";
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    MultiValueMap<String, String> map= new LinkedMultiValueMap<>();
    map.add("email", "844072586@qq.com");

    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(map, headers);
    ResponseEntity<String> response = restTemplate.postForEntity( url, request , String.class );
    System.out.println(response.getBody());
}

Результаты:

{"status":500,"msg":"该邮箱已被注册","data":null}

в коде,MultiValueMapдаMapподкласс , один из егоkeyможет хранить несколькоvalue, просто посмотрите на этот интерфейс:

public interface MultiValueMap<K, V> extends Map<K, List<V>> {...}

зачем использоватьMultiValueMap?так какHttpEntityПринятый тип запроса это.

public HttpEntity(@Nullable T body, @Nullable MultiValueMap<String, String> headers){}
我这里只展示它的一个construct,从它可以看到我们传入的map是请求体,headers是请求头。

зачем использоватьHttpEntityПотому чтоrestTemplate.postForEntityметод, хотя запрос, полученный на поверхности,@Nullable Object requestтип, но если вы отследите, то обнаружите, что этоrequestиспользуетсяHttpEntityанализировать. Основной код выглядит следующим образом:

if (requestBody instanceof HttpEntity) {
	this.requestEntity = (HttpEntity<?>) requestBody;
}else if (requestBody != null) {
	this.requestEntity = new HttpEntity<>(requestBody);
}else {
	this.requestEntity = HttpEntity.EMPTY;
}

Я попытался использовать карту для передачи параметров, компиляция не сообщит об ошибке, но не может быть выполнена, это недопустимый запрос запроса URL (400 ERROR). На самом деле этот метод запроса удовлетворил почтовый запрос, и куки также являются частью заголовка. Заголовок запроса и тело запроса могут быть установлены по мере необходимости. Другие методы аналогичны.

4. Используйте exchange, чтобы указать метод вызова

Отличие метода exchange() от описанных выше методов getForObject(), getForEntity(), postForObject(), postForEntity() заключается в том, что он может указывать HTTP-тип запроса.

Но вы обнаружите, что методы обмена, кажется,@Nullable HttpEntity<?> requestEntityЭтот параметр означает, что мы по крайней мере используем HttpEntity для передачи тела запроса.Я упоминал исходный код ранее, поэтому рекомендуется использовать HttpEntity для повышения производительности.

Пример

    @Test
    public void rtExchangeTest() throws JSONException {
        RestTemplate restTemplate = new RestTemplate();
        String url = "http://xxx.top/notice/list";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
        JSONObject jsonObj = new JSONObject();
        jsonObj.put("start",1);
        jsonObj.put("page",5);

        HttpEntity<String> entity = new HttpEntity<>(jsonObj.toString(), headers);
        ResponseEntity<JSONObject> exchange = restTemplate.exchange(url, 
                                          HttpMethod.GET, entity, JSONObject.class);
        System.out.println(exchange.getBody());
    }

Как видите, на этот раз я использовалJSONObjectОбъекты передаются и возвращаются.

Конечно, существует много методов HttpMethod, и их использование аналогично.

5. execute() указывает вызывающий метод

excute()использование иexchange()Почти так же, он также может указывать разныеHttpMethod, разница в том, что объект, который он возвращает, является объектом, сопоставленным телом ответа.<T>, вместоResponseEntity<T>.

Следует подчеркнуть, что,execute()Метод — это базовый вызов всех вышеперечисленных методов. Просто посмотрите на один:

	@Override
	@Nullable
	public <T> T postForObject(String url, @Nullable Object request, Class<T> responseType, Map<String, ?> uriVariables)
			throws RestClientException {

		RequestCallback requestCallback = httpEntityCallback(request, responseType);
		HttpMessageConverterExtractor<T> responseExtractor =
				new HttpMessageConverterExtractor<>(responseType, getMessageConverters(), logger);
		return execute(url, HttpMethod.POST, requestCallback, responseExtractor, uriVariables);
	}

Обратите внимание на публичный номер, ответьтеjava架构Получите ресурс видео архитектуры.