spring 发送http请求
Posted QQ_851228082
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 发送http请求相关的知识,希望对你有一定的参考价值。
spring 发送http的方法
spring发送http一共有两个方式,RestTemplate、WebClient。
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 template和uri 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请求的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Spring Boot 框架中发送 HTTP 请求?
求教golang中http发送post请求gb2312编码的解决方案
使用 Spring Security 配置应用程序以接受 HTTP 请求并发送 HTTPS 响应