文章目录
- form-data 数据请求格式样例
- 报错信息: **MissingServletRequestParameterException**解决方法
- 报错信息: **no multipart boundary was found** 解决方法
- Java代码实现
- 【错误】使用 UrlEncodedFormEntity 、BasicNameValuePair 请求失败(error)
- 【正确】使用 MultipartEntityBuilder 构造 boundary
- 总结
原创不易,转载请注明出处:
https://zhangxiaofan.blog.csdn.net/article/details/140737900
form-data 数据请求格式样例
我们先看下 form-data 数据的格式是长什么样的。
例如要传输 form-data 的键值对是:
a: aaa
b: bbb
请求的boundary设置如下:
addHeader(“Content-type”, “multipart/form-data;boundary=----12345”)
实际请求格式会加上boundary
,请求示例如下
----12345
Content-Disposition: form-data; name="a"
aaa
----12345
Content-Disposition: form-data; name="b"
bbb
postman自动生成的 boundary 如下:
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Type: multipart/form-data; boundary=--------------------------477613155954799910398847
报错信息: MissingServletRequestParameterException解决方法
org.springframework.web.bind.MissingServletRequestParameterException
请求Headers中的Content-Type类型不对
addHeader(“Content-Type”, “application/json; charset=UTF-8”);
将Json
换成multipart/form-data
addHeader(“Content-type”, “multipart/form-data; charset=UTF-8; boundary=” + new UUIDGenerator().next());
报错信息: no multipart boundary was found 解决方法
org.apache.tomcat.util.http.fileupload.FileUploadException: the request was rejected because no multipart boundary was found
这个报错是没有设置边界分隔符
解决方法:
- headers中添加boundary
addHeader("Content-type", "multipart/form-data;boundary=" + boundary)
- multipart/form-data 请求参数添加boundary
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create()
.setCharset(StandardCharsets.UTF_8)
// [重要]:设置 setBoundary 边界分隔符
.setBoundary(boundary);
Java代码实现
【错误】使用 UrlEncodedFormEntity 、BasicNameValuePair 请求失败(error)
public static JSONObject test(String URL) throws IOException {
RequestConfig requestConfig = RequestConfig.custom().build();
try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
HttpPost post = new HttpPost(URL);
List<NameValuePair> list = new ArrayList<>();
BasicNameValuePair pair1 = new BasicNameValuePair("a", "aaa");
BasicNameValuePair pair2 = new BasicNameValuePair("b", "bbb");
list.add(pair1);
list.add(pair2);
UrlEncodedFormEntity urlEncodedFormEntity = new UrlEncodedFormEntity(list,"UTF-8");
post.setEntity(urlEncodedFormEntity);
String boundary = new UUIDGenerator().next();
// 设置请求格式 multipart/form-data
post.addHeader("Content-type", "multipart/form-data;boundary=" + boundary);
post.addHeader("Accept", "*/*");
// UTF-8 解决中文乱码
post.addHeader("Accept-Encoding", "UTF-8");
post.addHeader("User-Agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36");
// 发送 post 请求
HttpResponse response = httpClient.execute(post);
...省略代码
}
上面的代码使用了 UrlEncodedFormEntity
,BasicNameValuePair
去设置请求格式 multipart/form-data,虽然在 Content-type
中设置了 boundary
,请求还是报请求参数错误:MissingServletRequestParameterException
是因为并没有在 multipart/form-data 的请求数据前后设置 分割边界符
【正确】使用 MultipartEntityBuilder 构造 boundary
要使用MultipartEntityBuilder
,先引入maven
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpclient</artifactId>
<version>4.5</version>
</dependency>
<dependency>
<groupId>org.apache.httpcomponents</groupId>
<artifactId>httpmime</artifactId>
<version>4.5</version>
</dependency>
java用httpclient(apache)完整的纯文本form-data请求实现如下:
public enum Test {
;
private static final Logger logger = LoggerFactory.getLogger(Test.class);
/**
* setConnectTimeout: 从客户端到url建立连接的超时时间
*/
private static final int CONNECT_TIMEOUT = 30 * 1000;
/**
* setSocketTimeout: 连接上一个url后,获取response的返回等待时间
*/
private static final int SOCKET_TIMEOUT = 3600 * 1000;
public static JSONObject postHttpFormDataPair(String URL, JSONObject requestBodyJson, Map<String, String> headersMap, String... saveRespHeaderName) {
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(CONNECT_TIMEOUT)
.setSocketTimeout(SOCKET_TIMEOUT)
.setCookieSpec(CookieSpecs.DEFAULT)
.build();
CookieStore cookieStore = new BasicCookieStore();
// 默认是 CloseableHttpClient httpClient = HttpClientBuilder.create().build();
try (CloseableHttpClient httpClient = createSSLClientDefaultBuilder()
.setDefaultRequestConfig(requestConfig)
.setDefaultCookieStore(cookieStore)
.build()) {
HttpPost post = new HttpPost(URL);
// [重要]:生成边界分隔符 boundary
String boundary = new UUIDGenerator().next();
// [重要]:设置请求格式 multipart/form-data
post.addHeader("Content-type", "multipart/form-data; charset=UTF-8; boundary=" + boundary);
post.addHeader("Accept", "*/*");
post.addHeader("Accept-Encoding", "UTF-8");
post.addHeader("User-Agent", " Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36");
if (!CollectionUtils.isEmpty(headersMap)) {
for (Map.Entry<String, String> entry : headersMap.entrySet()) {
post.addHeader(entry.getKey(), entry.getValue());
}
}
// 构造 formdata 请求数据
MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create()
.setCharset(StandardCharsets.UTF_8)
// [重要]:设置 setBoundary 边界分隔符
.setBoundary(boundary);
multipartEntityBuilder.addTextBody("a", "aaa");
multipartEntityBuilder.addTextBody("b", "bbb");
HttpEntity postFormDataBody = multipartEntityBuilder.build();
// 请求参数
post.setEntity(postFormDataBody);
// 发送 post 请求
HttpResponse response = httpClient.execute(post);
if (response == null) {
throw new RuntimeException("response is null");
} else if (response.getStatusLine().getStatusCode() == HttpStatus.SC_OK) {
return getResponseJsonObject(response, saveRespHeaderName);
} else {
logger.warn("response status is not 200");
return getResponseJsonObject(response, saveRespHeaderName);
}
} catch (Exception ex) {
logger.error("error:", ex);
throw new RuntimeException("系统异常");
}
}
@NotNull
private static JSONObject getResponseJsonObject(HttpResponse response, String[] saveRespHeaderName) throws IOException {
InputStream in = response.getEntity().getContent();
BufferedReader reader = new BufferedReader(new InputStreamReader(in));
String lines;
StringBuilder responseMsg = new StringBuilder("");
while ((lines = reader.readLine()) != null) {
lines = new String(lines.getBytes(), StandardCharsets.UTF_8);
responseMsg.append(lines);
}
reader.close();
in.close();
JSONObject jsonObject = new JSONObject();
jsonObject.put("message", responseMsg.toString());
// save header
for (String name : saveRespHeaderName) {
jsonObject.put(name, response.getHeaders(name));
}
return jsonObject;
}
/**
* 绕过https证书校验
*/
public static HttpClientBuilder createSSLClientDefaultBuilder() {
HttpClientBuilder httpClientBuilder = null;
try {
SSLContextBuilder builder = new SSLContextBuilder();
// 实现该接口,证书受信任的X509证书校验为true
builder.loadTrustMaterial(null, (chain, authType) -> true);
// 创建HttpsURLConnection对象,并设置其SSLSocketFactory对象
SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(builder.build(), new String[]{"TLSv1.2"}, null, NoopHostnameVerifier.INSTANCE);
// HttpsURLConnection对象就可以正常连接HTTPS了,无论其证书是否经权威机构的验证,只要实现了接口X509TrustManager的类MyX509TrustManager信任该证书。
httpClientBuilder = HttpClients.custom().setSSLSocketFactory(socketFactory);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("系统异常");
}
return httpClientBuilder;
}
}
总结
想要代码实现 form-data
格式的请求要注意下面2点:
- 设置
boundary
边界分隔符 - 设置
Content-type