spring 发送http请求

Posted QQ_851228082

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 发送http请求相关的知识,希望对你有一定的参考价值。

spring 发送http的方法

spring发送http一共有两个方式,RestTemplateWebClient

RestTemplate

RestTemplate是spring推出的第一代Rest Client,它只能发送同步http请求,基于底层httpclient库,它暴露了同步的、简单的模板方法。
RestTemplate默认使用java.net.HttpURLConnection来发送请求,可以使用其它实现了ClientHttpRequestFactory接口http库,比如Apache HttpComponents、Netty、OkHttp,例如使用Apache HttpComponents

RestTemplate template = new RestTemplate(new HttpComponentsClientHttpRequestFactory());

ClientHttpRequestFactory 暴露了配置选项,比如凭证、连接池。java.net实现版本对错误的状态代码(比如401)会抛出异常,如果这是一个问题,请切换为其他http库。

uri

RestTemplate可以接受uri templateuri template variable两个参数,uri template variable就是uri的参数值,要么是String可变参数、要么就是Map<String,String>

  • String可变参数
//42,21就是uri template variable
String result = restTemplate.getForObject(
        "https://example.com/hotels/{hotel}/bookings/{booking}", String.class, "42", "21");
Map<String, String> vars = Collections.singletonMap("hotel", "42");
  • Map<String,String>
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject(
        "https://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
  • uri template会自动编码
restTemplate.getForObject("https://example.com/hotel list", String.class);
// Results in request to "https://example.com/hotel%20list"

Header

使用exchange方法来指定请求头

String uriTemplate = "https://example.com/hotels/{hotel}";
URI uri = UriComponentsBuilder.fromUriString(uriTemplate).build(42);
RequestEntity<Void> requestEntity = RequestEntity.get(uri)
        .header(("MyRequestHeader", "MyValue")
        .build();
ResponseEntity<String> response = template.exchange(requestEntity, String.class);
String responseHeader = response.getHeaders().getFirst("MyResponseHeader");
String body = response.getBody();

Body

RestTemplate使用HttpMessageConverter来序列化和反序列化请求和响应数据。
发送post时,对象被序列化到请求体,比如

//person被序列化到请求体中
URI location = template.postForLocation("https://example.com/people", person);

你不需要明确设置Content-Type请求头,在大多数情况下,根据请求方法参数Object request选择message converter,message converter会相应的设置Content-Type请求头。如有必要你可以使用exchange方法来明确提供Content-Type请求头;

Get方法时,响应体会反序列化为一个输出对象,如下

Person person = restTemplate.getForObject("https://example.com/people/{id}", Person.class, 42);

Accept 请求头不需要显式设置,在大多数情况下,基于请求方法参数responseType会选择一个兼容的message converter,它会添加Accept头。可使用exchange方法显式指定Accept头。

MessageConverter

Spring-web模块包含HttpMessageConverter约定,约定指的是通过InputStream和OutputStream,HttpMessageConverter如何读取http请求body和写入http响应。
spring web框架提供了主要MIME type的HttpMessageConverter实现,在客户端RestTemplate来注册这些实现,在服务器端RequestMethodHandlerAdapter 来注册。
内建的MessageConverter有

  • StringHttpMessageConverter,读取及写入string,支持读取所有的MIME type(text/*),写入到响应时Content-Type : text/plain.
  • FormHttpMessageConverter,支持Content-Type:application/x-www-form-urlencoded,也支持multipart/form-data
  • ByteArrayHttpMessageConverter,支持Content-Type:application/octet-stream,可以从请求读取字节数组,可以将字节数组写入响应
  • MarshallingHttpMessageConverter,支持Content-Type为text/xml 、application/xml.
  • MappingJackson2HttpMessageConverter,支持json
  • MappingJackson2XmlHttpMessageConverter,支持Content-Type为application/xml.
  • SourceHttpMessageConverter,支持Content-Type为text/xml 、application/xml.
  • BufferedImageHttpMessageConverter,支持从请求和响应读取或写入java.awt.image.BufferedImage

Jackson JSON Views

可以指定Jackson JSON Views来序列化一部分对象属性而不是全部

MappingJacksonValue value = new MappingJacksonValue(new User("eric", "7!jd#h23"));
value.setSerializationView(User.WithoutPasswordView.class);
RequestEntity<MappingJacksonValue> requestEntity =
    RequestEntity.post(new URI("https://example.com/user")).body(value);
ResponseEntity<String> response = template.exchange(requestEntity, String.class);

Multipart

使用MultiValueMap<String, Object>发送Multipart Data,map中的值可以是Object(表示部分内容)、可以是Resource(表示文件)、可以是HttpEntity (表示带有header的部分内容)

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>();

parts.add("fieldPart", "fieldValue");
parts.add("filePart", new FileSystemResource("...logo.png"));
parts.add("jsonPart", new Person("Jason"));

HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_XML);
parts.add("xmlPart", new HttpEntity<>(myBean, headers));

在大多数情况下不需要设置Content-Type,基于选择的HttpMessageConverter会决定Content-Type,比如Resource的时候,根据文件后缀决定;
如果MultiValueMap包含至少一个非String值,那么Content-Type会被设置为multipart/form-data ,如果MultiValueMap 都是String 那么 Content-Type默认是 application/x-www-form-urlencoded. 如果有必要, Content-Type也可以明确指定.

WebClient

WebClient是一个non-blocking,响应式客户端,支持同步、异步请求和流式的场景

In contrast to RestTemplate, WebClient supports the following:

  • Non-blocking I/O.
  • Reactive Streams back pressure.
  • High concurrency with fewer hardware resources.
  • Functional-style, fluent API that takes advantage of Java 8 lambdas.
  • Synchronous and asynchronous interactions.
  • Streaming up to or streaming down from a server.

创建WebClient

最简单的方式通过静态工厂方法

WebClient.create()
WebClient.create(String baseUrl)

也可以使用builder(),这种有更多选项

uriBuilderFactory: Customized UriBuilderFactory to use as a base URL.

defaultUriVariables: default values to use when expanding URI templates.

defaultHeader: Headers for every request.

defaultCookie: Cookies for every request.

defaultRequest: Consumer to customize every request.

filter: Client filter for every request.

exchangeStrategies: HTTP message reader/writer customizations.

clientConnector: HTTP client library settings.

比如,

WebClient client = WebClient.builder()
        .codecs(configurer -> ... )
        .build();

一旦创建,WebClient就不能更改了,但可以copy然后再修改

WebClient client1 = WebClient.builder()
        .filter(filterA).filter(filterB).build();

WebClient client2 = client1.mutate()
        .filter(filterC).filter(filterD).build();

// client1 has filterA, filterB
// client2 has filterA, filterB, filterC, filterD

//retrieve方法用来解压出response

WebClient client = WebClient.create("https://example.org");

Mono<ResponseEntity<Person>> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .toEntity(Person.class);

//仅得到body

Mono<Person> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .bodyToMono(Person.class);

//默认的4xx和5xx会引起WebClientResponseException,可以自定义错误处理

Mono<Person> result = client.get()
        .uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
        .retrieve()
        .onStatus(HttpStatus::is4xxClientError, response -> ...)
        .onStatus(HttpStatus::is5xxServerError, response -> ...)
        .bodyToMono(Person.class);

发送同步请求

Person person = client.get().uri("/person/{id}", i).retrieve()
    .bodyToMono(Person.class)
    .block();
List<Person> persons = client.get().uri("/persons").retrieve()
    .bodyToFlux(Person.class)
    .collectList()
    .block();

总结

  • RestTemplate是spring推出的第一代rest client,它提供了简单的、同步的请求模板方法,默认的不用设置Content-Type,根据请求参数会自动设置Content-Type,如果MultiValueMap有非String值,则发送Multipart/form-data,如果是字符串,则默认是键值对形式(Content-Type:application/x-www-form-urlencoded),也可以明确指定。使用execute、change是通用请求方法,可以指定content-type等参数。
  • WebClient是spring推出的第二代rest client,提供了同步的、异步的、非阻塞式http请求方法,属于Spring WebFlux的一部分,目前项目中并没有IO密集型的业务,所以暂时不用这种方式了,继续使用RestTemplate

以上是关于spring 发送http请求的主要内容,如果未能解决你的问题,请参考以下文章

无法将http post请求发送到spring boot

如何在 Spring Boot 框架中发送 HTTP 请求?

求教golang中http发送post请求gb2312编码的解决方案

使用 Spring Security 配置应用程序以接受 HTTP 请求并发送 HTTPS 响应

使用 spring webclient 对 http 请求进行可读的调试日志记录

使用PHP中的curl发送请求