【Http请求-Spring RestTemplate】3.常见问题解决方案

服务端获取不到body的问题:

这个问题坑了我两天了,用RestTemplate、HttpClient、JDK URLConnection写测试类,都试了,一旦设置header的contentType为JSON,服务端拦截器中通过request.getParameterMap()就为{},问题是同样的代码在别人的项目里都能获取到。一开始觉得问题在客户端,试验了十几种设置都还是不能获取,但是仔细看了服务端的代码,发现Conontroller中@RequestBody的地方就能获取。考虑我的项目是Spring3.0.5RELEASE版本且耦合代码不能升级,服务端是4.3.3。

1.网上查了下发现4.3.3的Restemplate的实现类引入了fastJson,可在restemplate.postForObject时自动将JSONString设值到parameterMap中去,3.0.5没这个功能导致。

2.服务端引入的javax.servlet.ServletRequest的jar是javax.servlet-api-3.1.0.jar;getParameterMap的原型为:

public Map<String, String[]> getParameterMap();

所以如果header的contentType不是JSON:requestEntity只能用List类型的MultiValueMap:

HttpEntity<MultiValueMap<String, Object>> requestEntity  = new HttpEntity<MultiValueMap<String, Object>>(postParameters, headers);

如果header的contentType为JSON,低版本(3.0.5)的spring-mvc.jar包中的Restemplate将不能设值到ParameterMap中。

解决方案:

服务端拦截器中兼容request.getReader()和request.getParameterMap()获取数据:

ImmutableMap.Builder<String, Object> paramBuilder = ImmutableMap.builder();
if(request.getParameterMap() == null || request.getParameterMap().isEmpty()){
    String reqJson = IOUtils.toString(request.getReader());
    JSONObject jsonObject = JSON.parseObject(reqJson);
    for(String ch:jsonObject.keySet()){
        paramBuilder.put(ch,jsonObject.get(ch));
    }
}else{
    Map<String, String[]> paramMap = request.getParameterMap();
    for(String ch:paramMap.keySet()){
        paramBuilder.put(ch,paramMap.get(ch));
    }
}

客户端以JSON方式调用即可!:

HttpHeaders headers = new HttpHeaders();

headers.setContentType(MediaType.APPLICATION_JSON);

HttpEntity<String> formEntity = new HttpEntity<String>(jsonStr, headers);
String result = restTemplate.postForObject(interUrl, formEntity, String.class);

所以这个坑了我两天的问题回过来看,有时问题不一定在客户端,服务端的兼容性也很重要!

 

 

 

RestTemplate时去提示信息错误

相同的参数(接口的入参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的过滤器:

1

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)

封包列表的面板中显示,编号,时间戳,源地址,目标地址,协议,长度,以及封包信息。 你可以看到不同的协议用了不同的颜色显示。

http://blog.csdn.net/holandstone/article/details/47026213

 

 

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的操作。

http://blog.csdn.net/u013979547/article/details/52564316 

问题描述

我没有找到任何例子来解决我的问题,所以我想请你帮忙。我不能简单地使用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我想你需要添加一个messageConverterMappingJacksonHttpMessageConverter

 

第五种方案

“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是一个很方便的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);

 

 

 

 

展开阅读全文

没有更多推荐了,返回首页