版权所有,转载请注明出处,谢谢!
https://blog.csdn.net/gxkvji/article/details/97966198
1.问题
调用第三方接口,接口要求为:http请求类型,post请求方式,请求头固定参数。
在使用常规的urlConnection请求不到接口,从网上找的使用httpclient解决。接口要求参数为json字符串,并且放在body里,在raw里放入参数json字符串,然后我在请求头里设置content-Type为application/json,但是返回结果就是缺少body。研究了半天,看了源码才解决。原来是发送过去的参数被序列化为表格参数了,并不是字符串。上代码,看了代码就明白了。
2.代码
引入maven依赖:
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpcore</artifactId>
<version>4.4.11</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5.7</version>
</dependency>
具体代码实现:
/**
* 向指定 URL 发送POST方法的请求(数据格式为json)
*
* @param url 发送请求的 URL
* @param paramMap 请求参数集合
* @param paramMap 请求参数,请求参数为json的形式。
* @return 所代表远程资源的响应结果
*/
public static String sendPost(String url, Map<String, Object> paramMap) {
CloseableHttpClient httpClient = null;
CloseableHttpResponse httpResponse = null;
String result = "";
// 创建httpClient实例
httpClient = HttpClients.createDefault();
// 创建httpPost远程连接实例
HttpPost httpPost = new HttpPost(url);
// 配置请求参数实例
RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(35000)// 设置连接主机服务超时时间
.setConnectionRequestTimeout(35000)// 设置连接请求超时时间
.setSocketTimeout(60000)// 设置读取数据连接超时时间
.build();
// 为httpPost实例设置配置
httpPost.setConfig(requestConfig);
// 设置请求头
httpPost.addHeader("Content-Type", "application/json");
httpPost.addHeader("appid", "avkp4qyu01fki");
// 封装post请求参数
JSONObject jsonObject = new JSONObject();
if (null != paramMap && paramMap.size() > 0) {
//List<NameValuePair> nvps = new ArrayList<NameValuePair>();
// 通过map集成entrySet方法获取entity
Set<Map.Entry<String, Object>> entrySet = paramMap.entrySet();
// 循环遍历,获取迭代器
Iterator<Map.Entry<String, Object>> iterator = entrySet.iterator();
while (iterator.hasNext()) {
Map.Entry<String, Object> mapEntry = iterator.next();
//nvps.add(new BasicNameValuePair(mapEntry.getKey(), mapEntry.getValue().toString()));
jsonObject.put(mapEntry.getKey(),mapEntry.getValue());
}
ContentType contentType = ContentType.create("application/json");
// 为httpPost设置封装好的请求参数
try {
//设置参数的content-type格式
httpPost.setEntity(new StringEntity(jsonObject.toString(), contentType));
//此方式参数content-type为application/x-www-form-urlencoded,源码中默认实现的
//httpPost.setEntity(new UrlEncodedFormEntity(nvps,"utf-8"));
} catch (Exception e) {
e.printStackTrace();
}
}
try {
// httpClient对象执行post请求,并返回响应参数对象
httpResponse = httpClient.execute(httpPost);
// 从响应对象中获取响应内容
HttpEntity entity = httpResponse.getEntity();
result = EntityUtils.toString(entity);
} catch (ClientProtocolException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
// 关闭资源
if (null != httpResponse) {
try {
httpResponse.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != httpClient) {
try {
httpClient.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return result;
}
public class UrlEncodedFormEntity extends StringEntity {
public UrlEncodedFormEntity(List<? extends NameValuePair> parameters, String charset) throws UnsupportedEncodingException {
//参数content默认为application/x-www-form-urlencoded
super(URLEncodedUtils.format(parameters, charset != null ? charset : HTTP.DEF_CONTENT_CHARSET.name()), ContentType.create("application/x-www-form-urlencoded", charset));
}
public UrlEncodedFormEntity(Iterable<? extends NameValuePair> parameters, Charset charset) {
super(URLEncodedUtils.format(parameters, charset != null ? charset : HTTP.DEF_CONTENT_CHARSET), ContentType.create("application/x-www-form-urlencoded", charset));
}
public UrlEncodedFormEntity(List<? extends NameValuePair> parameters) throws UnsupportedEncodingException {
this((Iterable)parameters, (Charset)((Charset)null));
}
public UrlEncodedFormEntity(Iterable<? extends NameValuePair> parameters) {
this((Iterable)parameters, (Charset)null);
}
}
当时没有想到看源码,一直以为是方法不对,在网上找对应的方法,找了半天发现3.1版本的可以设置请求体body,结果找不到jar包。然后才通过debug发现给httpPost设置请求参数时,参数的content-type是application/x-www-form-urlencoded,然后看了UrlEncodedFormEntity 类的源码,从名字上看也是可以看出是表单序列化参数,是StringEntity的一个子类,然后我就看有没有content/json格式的子类实现,发现没有,然后直接使用了StringEntity,可以设置content格式,这样问题就解决了。
现在看一下问题不难,结果搞了大半天没搞定,养成一个看源码的习惯还是很重要的。