相同的参数(接口的入参json打印在日志了)在PostMan中返回预期的数据,但使用RestTemplate时去提示信息错误(参数中汉字)。
这种情况,搞得怀疑对RestTemplate的理解了
使用RestTemplate的代码如下:
JSONObject reqVO = new JSONObject(12);
reqVO.put("token", smsConfig.getToken());
reqVO.put("phones", new String[]{mobile.toString()});//一个或多个号码数组,一次不能超过10
reqVO.put("content", "content,包含汉字");
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
String jsonPost = reqVO.toString();
HttpEntity<String> entity = new HttpEntity<>(jsonPost, headers);
ResponseEntity<String> responseEntity = restTemplate.postForEntity(smsConfig.getUrl(), entity, String.class);
String body = responseEntity.getBody();
解决办法,通过wireshark抓包:
使用Postman发送时情况:
使用上面的代码调接口时的http数据情况:
/**
* A String equivalent of {@link MediaType#APPLICATION_JSON}.
* @see #APPLICATION_JSON_UTF8_VALUE
*/
public final static String APPLICATION_JSON_VALUE = "application/json";
/**
* Public constant media type for {@code application/json;charset=UTF-8}.
*/
public final static MediaType APPLICATION_JSON_UTF8;
只更改上面中设置Content-type的这行代码,更改后的:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON_UTF8);
上面 wireshark的过滤器:
ip.dst==目标接口的ip地址 and tcp.port==80 and http.request.method="POST"
Tips:
wireshark是非常流行的网络封包分析软件,功能十分强大。可以截取各种网络封包,显示网络封包的详细信息。使用wireshark的人必须了解网络协议,否则就看不懂wireshark了。
为了安全考虑,wireshark只能查看封包,而不能修改封包的内容,或者发送封包。
wireshark能获取HTTP,也能获取HTTPS,但是不能解密HTTPS,所以wireshark看不懂HTTPS中的内容,总结,如果是处理HTTP,HTTPS 还是用Fiddler, 其他协议比如TCP,UDP 就用wireshark.
过滤表达式的规则
表达式规则
1. 协议过滤
比如TCP,只显示TCP协议。
2. IP 过滤
比如 ip.src ==192.168.1.102 显示源地址为192.168.1.102,
ip.dst==192.168.1.102, 目标地址为192.168.1.102
3. 端口过滤
tcp.port ==80, 端口为80的
tcp.srcport == 80, 只显示TCP协议的愿端口为80的。
4. Http模式过滤
http.request.method=="GET", 只显示HTTP GET方法的。
5. 逻辑运算符为 AND/ OR
常用的过滤表达式
过滤表达式 | 用途 |
http | 只查看HTTP协议的记录 |
ip.src ==192.168.1.102 or ip.dst==192.168.1.102 | 源地址或者目标地址是192.168.1.102 |
| |
| |
封包列表(Packet List Pane)
封包列表的面板中显示,编号,时间戳,源地址,目标地址,协议,长度,以及封包信息。 你可以看到不同的协议用了不同的颜色显示。
restTemplate使用及中文乱码问题
public <T> T restTemplate(String url, Map<String,T> params, Class<T> var, HttpMethod method) {
RestTemplate restTemplate = new RestTemplate();
FormHttpMessageConverter fc = new FormHttpMessageConverter();
StringHttpMessageConverter s = new StringHttpMessageConverter(StandardCharsets.UTF_8);
List<HttpMessageConverter<?>> partConverters = new ArrayList<HttpMessageConverter<?>>();
partConverters.add(s);
partConverters.add(new ResourceHttpMessageConverter());
fc.setPartConverters(partConverters);
restTemplate.getMessageConverters().addAll(Arrays.asList(fc, new MappingJackson2HttpMessageConverter()));
MultiValueMap<String, T> map = new LinkedMultiValueMap<>();
map.setAll(params);
switch (method) {
case POST:
return restTemplate.postForObject(url, map, var);
case GET:
String getParams = "?" + map.keySet().stream().map(k -> String.format("%s={%s}", k, k)).collect(Collectors.joining("&"));
return restTemplate.getForObject(url + getParams, var, params);
default:
return restTemplate.postForObject(url, map, var);
}
}
所要注意的是get请求要求我们对URL中参数用占位符封装,user/getUser?userId={userId}&fe=
{fe},就像这样,所以我在封装get请求时有一个拼接URL的操作。
问题描述
我没有找到任何例子来解决我的问题,所以我想请你帮忙。我不能简单地使用JSON中的RestTemplate对象发送POST请求
每次我得到org.springframework.web.client.HttpClientErrorException:415不支持的媒体类型
我以这种方式使用RestTemplate:
...
restTemplate = new RestTemplate();
List<HttpMessageConverter<?>> list = new ArrayList<HttpMessageConverter<?>>();
list.add(new MappingJacksonHttpMessageConverter());
restTemplate.setMessageConverters(list);
...
Payment payment= new Payment("Aa4bhs");
Payment res = restTemplate.postForObject("http://localhost:8080/aurest/rest/payment", payment, Payment.class);
我的错是什么
最佳解决方案
这种技术对我有用:
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers);
restTemplate.put(uRL, entity);
我希望这有帮助
KD
次佳解决方案
我一直在使用具有JSONObjects的rest模板,如下所示:
// create request body
JSONObject request = new JSONObject();
request.put("username", name);
request.put("password", password);
// set headers HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> entity = new HttpEntity<String>(request.toString(), headers); // send request and parse result ResponseEntity<String> loginResponse = restTemplate .exchange(urlString, HttpMethod.POST, entity, String.class); if (loginResponse.getStatusCode() == HttpStatus.OK) { JSONObject userJson = new JSONObject(loginResponse.getBody()); } else if (loginResponse.getStatusCode() == HttpStatus.UNAUTHORIZED) { // nono... bad credentials }
第三种解决方案
尝试调试REST端点时,我遇到这个问题。这是使用Spring的RestTemplate类来创建我使用的POST请求的一个基本示例。我花了很长时间把不同地方的代码整理成一个工作版本。
RestTemplate restTemplate = new RestTemplate();
String url = "endpoint url";
String requestJson = "{\"queriedQuestion\":\"Is there pain in your hand?\"}"; HttpHeaders headers = new HttpHeaders(); headers.setContentType(MediaType.APPLICATION_JSON); HttpEntity<String> entity = new HttpEntity<String>(requestJson,headers); String answer = restTemplate.postForObject(url, entity, String.class); System.out.println(answer);
特定的JSON解析器我的休息终点是使用围绕字段名称的双引号,这就是为什么我在我的requestJson String中转义了双引号。
第四种方案
根据指定的here我想你需要添加一个messageConverter
为MappingJacksonHttpMessageConverter
第五种方案
“415不支持的媒体类型”错误告诉您服务器将不接受您的POST请求。您的请求绝对不错,这是mis-configured的服务器。
MappingJacksonHttpMessageConverter
会自动将请求content-type标头设置为application/json
,我的猜测是您的服务器拒绝了。你没有告诉我们任何关于你的服务器设置,所以我不能真的建议你。
第六种方案
如果您使用的是Spring 3.0,则可以避免使用org.springframework.web.client.HttpClientErrorException:415不支持的介质类型异常的简单方法是将jackson jar文件包含在类路径中,并使用mvc:annotation-driven
config元素。 As specified here。
我正在拉我的头发,试图找出为什么mvc-ajax应用程序工作没有任何特殊的配置为MappingJacksonHttpMessageConverter
。如果你仔细阅读我所链接的文章:
Underneath the covers, Spring MVC delegates to a HttpMessageConverter to perform the serialization. In this case, Spring MVC invokes a MappingJacksonHttpMessageConverter built on the Jackson JSON processor. This implementation is enabled automatically when you use the mvc:annotation-driven configuration element with Jackson present in your classpath.
参考文献
注:本文内容整合自google/baidu/bing辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:gxnotes#qq.com(#替换为@)。
本文由 《共享笔记》整理, 博文地址: https://gxnotes.com/article/61287.html
https://gxnotes.com/article/61287.html
RestTemplate
这篇文章打算介绍一下Spring的RestTemplate
。我这边以前设计到http交互的,之前一直采用的是Apache HttpComponents 。后来发现Spring框架中已经为我们封装好了这个框架。因此我们就不需要直接使用下面这种稍微底层一点的方式来实现我们的功能:
String uri = "http://example.com/hotels/1/bookings";
PostMethod post = new PostMethod(uri); String request = // create booking request content post.setRequestEntity(new StringRequestEntity(request)); httpClient.executeMethod(post); if (HttpStatus.SC_CREATED == post.getStatusCode()) { Header location = post.getRequestHeader("Location"); if (location != null) { System.out.println("Created new booking at :" + location.getValue()); } }
Spring的RestTemplate提供了一些更高级别的方法来满足我们的功能,比如对HTTP Method的支持:
虽然Spring的RestTemplate提供了对这么多HTTP method的支持,但是从个人工作角度来说,常用的也就get和post这两种方式,有兴趣的朋友可以自己翻看一下源码。
RestTemplate的使用
RestTemplate有两个构造方法,分别是:
public RestTemplate() {
/**
...初始化过程
*/
}
public RestTemplate(ClientHttpRequestFactory requestFactory) { this(); setRequestFactory(requestFactory); }
public RestTemplate() {
/**
...初始化过程
*/
}
public RestTemplate(ClientHttpRequestFactory requestFactory) { this(); setRequestFactory(requestFactory); }
其中,第二个构造方法中可以传入ClientHttpRequestFactory参数,第一个进行默认初始化,因为我们经常需要对请求超时进行设置并能够对超时进行后续处理,而第一个构造方法,我们无法控制超时时间,第二个构造中的ClientHttpRequestFactory接口的实现类中存在timeout属性,因此选用第二个构造方法。
在spring配置文件中进行如下配置:
<!-- 配置RestTemplate -->
<!--Http client Factory-->
<bean id="httpClientFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory">
<property name="connectTimeout" value="${connectTimeout}"/> <property name="readTimeout" value="${readTimeout}"/> </bean> <!--RestTemplate--> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <constructor-arg ref="httpClientFactory"/> </bean>
<!-- 配置RestTemplate -->
<!--Http client Factory-->
<bean id="httpClientFactory" class="org.springframework.http.client.SimpleClientHttpRequestFactory">
<property name="connectTimeout" value="${connectTimeout}"/> <property name="readTimeout" value="${readTimeout}"/> </bean> <!--RestTemplate--> <bean id="restTemplate" class="org.springframework.web.client.RestTemplate"> <constructor-arg ref="httpClientFactory"/> </bean>
当然也可以直接使用:
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
SimpleClientHttpRequestFactory requestFactory = new SimpleClientHttpRequestFactory();
requestFactory.setConnectTimeout(1000);
requestFactory.setReadTimeout(1000);
RestTemplate restTemplate = new RestTemplate(requestFactory);
注意:ClientHttpRequestFactory 接口有4个实现类,分别是:
- AbstractClientHttpRequestFactoryWrapper 用来装配其他request factory的抽象类。
- CommonsClientHttpRequestFactory 允许用户配置带有认证和http连接池的httpclient,已废弃,推荐用HttpComponentsClientHttpRequestFactory。
- HttpComponentsClientHttpRequestFactory 同2.
- SimpleClientHttpRequestFactory 接口的一个简单实现,可配置proxy,connectTimeout,readTimeout等参数。
GET
Spring的RestTemplate提供了许多的支持,这里仅仅列出常用的接口:
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException
public <T> T getForObject(String url, Class<T> responseType, Object... urlVariables) throws RestClientException
public <T> T getForObject(String url, Class<T> responseType, Map<String, ?> urlVariables) throws RestClientException public <T> T getForObject(URI url, Class<T> responseType) throws RestClientException
对于GET请求来说,我一般常用的几种形式如下:
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/bookings/{booking}", String.class,"42", "21");
或者下面这张形式:
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
Map<String, String> vars = Collections.singletonMap("hotel", "42");
String result = restTemplate.getForObject("http://example.com/hotels/{hotel}/rooms/{hotel}", String.class, vars);
以及:java String message = restTemplate.getForObject("http://localhost:8080/yongbarservice/appstore/appgoods/restTemplate?name=zhaoshijie&id=80", String.class );
他这种做法参考了uri-templates
(https://code.google.com/p/uri-templates/)这个项目。
POST
Spring的RestTemplate对post的常用接口:
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException
public <T> T postForObject(String url, Object request, Class<T> responseType, Object... uriVariables)
throws RestClientException
public <T> T postForObject(String url, Object request, Class<T> responseType, Map<String, ?> uriVariables) throws RestClientException public <T> T postForObject(URI url, Object request, Class<T> responseType) throws RestClientException
我一般常用的方法为:
MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<String, String>(); bodyMap.setAll(urlVariables); ResponseClass responseClass = restTemplate.postForObject(CAR_CES_URL, bodyMap, ResponseClass.class);
以及:
HttpHeaders headers = new HttpHeaders();
headers.add("X-Auth-Token", "e348bc22-5efa-4299-9142-529f07a18ac9");
MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>(); postParameters.add("owner", "11"); postParameters.add("subdomain", "aoa"); postParameters.add("comment", ""); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(postParameters, headers); ParseResultVo exchange = null; try { exchange = restTemplate.postForObject("http://l-dnsutil1.ops.beta.cn6.qunar.com:10085/v1/cnames/tts.piao", requestEntity, ParseResultVo.class); logger.info(exchange.toString()); } catch (RestClientException e) { logger.info("。。。。"); }
HttpHeaders headers = new HttpHeaders();
headers.add("X-Auth-Token", "e348bc22-5efa-4299-9142-529f07a18ac9");
MultiValueMap<String, String> postParameters = new LinkedMultiValueMap<String, String>(); postParameters.add("owner", "11"); postParameters.add("subdomain", "aoa"); postParameters.add("comment", ""); HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<MultiValueMap<String, String>>(postParameters, headers); ParseResultVo exchange = null; try { exchange = restTemplate.postForObject("http://l-dnsutil1.ops.beta.cn6.qunar.com:10085/v1/cnames/tts.piao", requestEntity, ParseResultVo.class); logger.info(exchange.toString()); } catch (RestClientException e) { logger.info("。。。。"); }
以及:
DomainParam domainParam = new DomainParam();
domainParam.setCustomerId(1);
//...
logger.info("...."); restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter()); restTemplate.getMessageConverters().add(new StringHttpMessageConverter()); String responseResult = restTemplate.postForObject(url, domainParam, String.class);
DomainParam domainParam = new DomainParam();
domainParam.setCustomerId(1);
//...
logger.info("...."); restTemplate.getMessageConverters().add(new MappingJacksonHttpMessageConverter()); restTemplate.getMessageConverters().add(new StringHttpMessageConverter()); String responseResult = restTemplate.postForObject(url, domainParam, String.class);
其他
PUT
方式:
restTemplate.put("http://localhost:8080/yongbarservice/appstore/appgoods/restTemplate?name=zhaoshijie&id=80" ,null);
restTemplate.put("http://localhost:8080/yongbarservice/appstore/appgoods/restTemplate?name=zhaoshijie&id=80" ,null);
DELETE
方式
//delete方法(注意:delete方法没有返回值,说明,id=0这个参数在服务器端可以不定义该参数,直接使用request获取)
// restTemplate.delete("http://localhost:8080/yongbarservice/appstore/appgoods/deleteranking?id=0");
//delete方法(注意:delete方法没有返回值,说明,id=0这个参数在服务器端可以不定义该参数,直接使用request获取)
// restTemplate.delete("http://localhost:8080/yongbarservice/appstore/appgoods/deleteranking?id=0");
参考资料:
- http://docs.spring.io/spring/docs/4.0.1.BUILD-SNAPSHOT/spring-framework-reference/htmlsingle/#rest-resttemplate
- http://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/web/client/RestTemplate.html
1. Overview
In this tutorial, we’re going to illustrate the broad range of operations where the Spring REST Client – RestTemplate – can be used, and used well.
For the API side of all examples, we’ll be running the RESTful service from here.
Further reading:
Basic Authentication with the RestTemplate
How to do Basic Authentication with the Spring RestTemplate.
RestTemplate with Digest Authentication
How to set up Digest Authentication for the Spring RestTemplate using HttpClient 4.
HttpClient 4 Tutorial
Comprehensive Guide to the Apache HttpClient - start with basic usage and make your way though the advanced scenarios.
2. Use GET to Retrieve Resources
2.1. Get Plain JSON
Let’s start simple and talk about GET requests – with a quick example using the getForEntity() API:
|
Notice that we have full access to the HTTP response – so we can do things like checking the status code to make sure the operation was actually successful, or work with the actual body of the response:
|
We’re working with the response body as a standard String here – and using Jackson (and the JSON node structure that Jackson provides) to verify some details.
2.1. Retrieving POJO Instead of JSON
We can also map the response directly to a Resource DTO – for example:
|
Now – we can simply use the getForObject API in the template:
|
3. Use HEAD to Retrieve Headers
Let’s now have a quick look at using HEAD before moving on to the more common methods – we’re going to be using the headForHeaders() API here:
|
4. Use POST to Create a Resource
In order to create a new Resource in the API – we can make good use of the postForLocation(), postForObject() or postForEntity() APIs.
The first returns the URI of the newly created Resource while the second returns the Resource itself.
4.1. The postForObject API
|
4.2. The postForLocation API
Similarly, let’s have a look at the operation that – instead of returning the full Resource, just returns the Location of that newly created Resource:
|
4.3. The exchange API
Finally, let’s have a look at how to do a POST with the more generic exchange API:
|
5. Use OPTIONS to get Allowed Operations
Next, we’re going to have a quick look at using an OPTIONS request and exploring the allowed operations on a specific URI using this kind of request; the API is optionsForAllow:
|
6. Use PUT to Update a Resource
Next, we’ll start looking at PUT – and more specifically the exchange API for this operation, because of the template.put API is pretty straightforward.
6.1. Simple PUT with .exchange
We’ll start with a simple PUT operation against the API – and keep in mind that the operation isn’t returning anybody back to the client:
|
6.2. PUT with .exchange and a Request Callback
Next, we’re going to be using a request callback to issue a PUT.
Let’s make sure we prepare the callback – where we can set all the headers we need as well as a request body:
|
Next, we create the Resource with POST request:
|
And then we update the Resource:
|
7. Use DELETE to Remove a Resource
To remove an existing Resource we’ll make short work of the delete() API:
|
8. Configure Timeout
We can configure RestTemplate to time out by simply using ClientHttpRequestFactory – as follows:
|
And we can use HttpClient for further configuration options – as follows:
|
9. Conclusion
We went over the main HTTP Verbs, using RestTemplate to orchestrate requests using all of these.
If you want to dig into how to do authentication with the template – check out my write-up on Basic Auth with RestTemplate.
The implementation of all these examples and code snippets can be found in my GitHub project
http://www.baeldung.com/rest-template
RestTemplate:
设置代理
Configure RestTemplate to Use a Proxy
As described in Section 33.1, “RestTemplate Customization”, you can use a RestTemplateCustomizer with RestTemplateBuilder to build a customized RestTemplate.
This is the recommended approach for creating a RestTemplate configured to use a proxy.
The exact details of the proxy configuration depend on the underlying client request factory that is being used.
The following example configures HttpComponentsClientRequestFactory with an HttpClient that uses a proxy for all hosts except 192.168.0.5:
static class ProxyCustomizer implements RestTemplateCustomizer {
@Override
public void customize(RestTemplate restTemplate) {
HttpHost proxy = new HttpHost("proxy.example.com");
HttpClient httpClient = HttpClientBuilder.create()
.setRoutePlanner(new DefaultProxyRoutePlanner(proxy) {
@Override
public HttpHost determineProxy(HttpHost target,
HttpRequest request, HttpContext context)
throws HttpException {
if (target.getHostName().equals("192.168.0.5")) {
return null;
}
return super.determineProxy(target, request, context);
}
}).build();
restTemplate.setRequestFactory(
new HttpComponentsClientHttpRequestFactory(httpClient));
}
}
RestTempate中处理返回值中带有泛型的API:
resttemplate是一个很方便的HTTP客户端,但是当返回的数据类型是泛型时会报错
//一般用法,通过postForObject获取结果
REST_TEMPLATE.postForObject(supplier.getApi(),param,Result.class)
//Result.java
public class Result<T> {
private int code;
private List<T> data;
...
}
//报错
java.lang.ClassCastException: java.util.LinkedHashMap cannot be cast to xxx
原因:
postForObject无法知道具体的实例化类型,解析为了LinkedHashMap
解决方法,使用exchange方法替代:
Map<String,Object> param = new HashedMap();
param.put("key","value");//传入参数
parameterizedTypeReference =
new ParameterizedTypeReference<Result<XXX>>(){};
//XXX为实例化的类型
HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
HttpEntity<String> entity = new HttpEntity<>(new Gson().toJson(param),headers);
ResponseEntity<YunResult<Instance>> result =
REST_TEMPLATE.exchange(url, HttpMethod.POST, entity,
parameterizedTypeReference);